备份CatanBuilding瘦身独立工程
This commit is contained in:
105
Assets/Scripts/UIExtend/ControlTextSize.cs
Normal file
105
Assets/Scripts/UIExtend/ControlTextSize.cs
Normal file
@@ -0,0 +1,105 @@
|
||||
using TMPro;
|
||||
using UnityEngine;
|
||||
using UnityEngine.UI;
|
||||
|
||||
[RequireComponent(typeof(TMP_Text))]
|
||||
public class ControlTextSize : MonoBehaviour, ILayoutElement
|
||||
{
|
||||
private TMP_Text curTxt;
|
||||
private bool cached = false;
|
||||
|
||||
public float maxPreferredWidth = 0f;
|
||||
public float maxPreferredHeight = 0f;
|
||||
|
||||
public float flexibleHeight
|
||||
{
|
||||
get
|
||||
{
|
||||
cacheReferences();
|
||||
return curTxt.flexibleHeight;
|
||||
}
|
||||
}
|
||||
|
||||
public float flexibleWidth
|
||||
{
|
||||
get
|
||||
{
|
||||
cacheReferences();
|
||||
return curTxt.flexibleWidth;
|
||||
}
|
||||
}
|
||||
|
||||
public int layoutPriority
|
||||
{
|
||||
get { return 1; }
|
||||
}
|
||||
|
||||
public float minHeight
|
||||
{
|
||||
get
|
||||
{
|
||||
cacheReferences();
|
||||
return curTxt.minHeight;
|
||||
}
|
||||
}
|
||||
|
||||
public float minWidth
|
||||
{
|
||||
get
|
||||
{
|
||||
cacheReferences();
|
||||
return curTxt.minWidth;
|
||||
}
|
||||
}
|
||||
|
||||
public float preferredHeight
|
||||
{
|
||||
get
|
||||
{
|
||||
cacheReferences();
|
||||
if (Mathf.Approximately(maxPreferredHeight, 0f))
|
||||
{
|
||||
return curTxt.preferredHeight;
|
||||
}
|
||||
else
|
||||
{
|
||||
return curTxt.preferredHeight > maxPreferredHeight ? maxPreferredHeight : curTxt.preferredHeight;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public float preferredWidth
|
||||
{
|
||||
get
|
||||
{
|
||||
cacheReferences();
|
||||
if (Mathf.Approximately(maxPreferredWidth, 0f))
|
||||
{
|
||||
return curTxt.preferredWidth;
|
||||
}
|
||||
else
|
||||
{
|
||||
return curTxt.preferredWidth > maxPreferredWidth ? maxPreferredWidth : curTxt.preferredWidth;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void CalculateLayoutInputHorizontal()
|
||||
{
|
||||
cacheReferences();
|
||||
}
|
||||
|
||||
public void CalculateLayoutInputVertical()
|
||||
{
|
||||
cacheReferences();
|
||||
}
|
||||
|
||||
private void cacheReferences()
|
||||
{
|
||||
if (!cached)
|
||||
{
|
||||
curTxt = gameObject.GetComponent<TMP_Text>();
|
||||
cached = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
11
Assets/Scripts/UIExtend/ControlTextSize.cs.meta
Normal file
11
Assets/Scripts/UIExtend/ControlTextSize.cs.meta
Normal file
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 924b8e04b3fc53e448dc2b03624e1291
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
46
Assets/Scripts/UIExtend/FloatExtensions.cs
Normal file
46
Assets/Scripts/UIExtend/FloatExtensions.cs
Normal file
@@ -0,0 +1,46 @@
|
||||
using System;
|
||||
using UnityEngine;
|
||||
|
||||
public static class FloatExtensions
|
||||
{
|
||||
public static string ToPercentageString(this float value)
|
||||
{
|
||||
int num = Mathf.RoundToInt(value * 100);
|
||||
return string.Format("{0}%", num);
|
||||
}
|
||||
}
|
||||
|
||||
public static class StringExtensions
|
||||
{
|
||||
/// <summary>
|
||||
/// 安全的字符串格式化扩展方法。如果格式化失败,返回原始格式字符串并输出错误日志。
|
||||
/// </summary>
|
||||
/// <param name="format">包含格式项的字符串。</param>
|
||||
/// <param name="args">要格式化的对象数组。</param>
|
||||
/// <returns>格式化后的字符串;如果格式化失败,返回原始格式字符串。</returns>
|
||||
public static string SafeFormat(this string format, params object[] args)
|
||||
{
|
||||
if (format == null)
|
||||
{
|
||||
Debug.LogError("Error: Format string is null.");
|
||||
return null;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
return string.Format(format, args);
|
||||
}
|
||||
catch (FormatException ex)
|
||||
{
|
||||
Debug.LogError($"String formatting failed: {ex.Message}\n" +
|
||||
$"Format string: {format}\n" +
|
||||
$"Argument count: {args?.Length ?? 0}");
|
||||
return format;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Debug.LogError($"Unknown error occurred during string formatting: {ex.Message}");
|
||||
return format;
|
||||
}
|
||||
}
|
||||
}
|
||||
11
Assets/Scripts/UIExtend/FloatExtensions.cs.meta
Normal file
11
Assets/Scripts/UIExtend/FloatExtensions.cs.meta
Normal file
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: fc5d46d525f921d43b539af107736c5f
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
772
Assets/Scripts/UIExtend/ImageMirror.cs
Normal file
772
Assets/Scripts/UIExtend/ImageMirror.cs
Normal file
@@ -0,0 +1,772 @@
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
using UnityEngine.Rendering;
|
||||
using UnityEngine.Sprites;
|
||||
using UnityEngine.UI;
|
||||
|
||||
/// <summary>
|
||||
/// The UI image mirror used to mirror image.
|
||||
/// </summary>
|
||||
[AddComponentMenu("Oasis/UI/Image Mirror")]
|
||||
[RequireComponent(typeof(RectTransform))]
|
||||
[RequireComponent(typeof(Image))]
|
||||
public sealed class ImageMirror : BaseMeshEffect, ILayoutElement
|
||||
{
|
||||
[SerializeField] [Tooltip("The mirror type.")]
|
||||
private MirrorModeType mirrorMode = MirrorModeType.Horizontal;
|
||||
|
||||
/// <summary>
|
||||
/// The mirror type.
|
||||
/// </summary>
|
||||
public enum MirrorModeType
|
||||
{
|
||||
/// <summary>
|
||||
/// Mirror horizontal mode.
|
||||
/// </summary>
|
||||
Horizontal,
|
||||
|
||||
/// <summary>
|
||||
/// Mirror vertical mode.
|
||||
/// </summary>
|
||||
Vertical,
|
||||
|
||||
/// <summary>
|
||||
/// Quarter mode.
|
||||
/// </summary>
|
||||
Quarter,
|
||||
|
||||
/// <summary>
|
||||
/// Mirror horizontal and inverse vertical mode.
|
||||
/// </summary>
|
||||
HorizontalInverse,
|
||||
|
||||
/// <summary>
|
||||
/// Mirror vertical and inverse horizontal mode.
|
||||
/// </summary>
|
||||
VerticalInverse,
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the mirror mode.
|
||||
/// </summary>
|
||||
public MirrorModeType MirrorMode
|
||||
{
|
||||
get => this.mirrorMode;
|
||||
|
||||
set
|
||||
{
|
||||
if (this.mirrorMode != value)
|
||||
{
|
||||
this.mirrorMode = value;
|
||||
this.graphic?.SetVerticesDirty();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
[System.Diagnostics.CodeAnalysis.SuppressMessage(
|
||||
"StyleCop.CSharp.NamingRules",
|
||||
"SA1300:ElementMustBeginWithUpperCaseLetter",
|
||||
Justification = "Reviewed. Suppression is OK here.")]
|
||||
public float flexibleHeight => -1;
|
||||
|
||||
/// <inheritdoc/>
|
||||
[System.Diagnostics.CodeAnalysis.SuppressMessage(
|
||||
"StyleCop.CSharp.NamingRules",
|
||||
"SA1300:ElementMustBeginWithUpperCaseLetter",
|
||||
Justification = "Reviewed. Suppression is OK here.")]
|
||||
public float flexibleWidth => -1;
|
||||
|
||||
/// <inheritdoc/>
|
||||
[System.Diagnostics.CodeAnalysis.SuppressMessage(
|
||||
"StyleCop.CSharp.NamingRules",
|
||||
"SA1300:ElementMustBeginWithUpperCaseLetter",
|
||||
Justification = "Reviewed. Suppression is OK here.")]
|
||||
public int layoutPriority => 0;
|
||||
|
||||
/// <inheritdoc/>
|
||||
[System.Diagnostics.CodeAnalysis.SuppressMessage(
|
||||
"StyleCop.CSharp.NamingRules",
|
||||
"SA1300:ElementMustBeginWithUpperCaseLetter",
|
||||
Justification = "Reviewed. Suppression is OK here.")]
|
||||
public float minHeight => 0;
|
||||
|
||||
/// <inheritdoc/>
|
||||
[System.Diagnostics.CodeAnalysis.SuppressMessage(
|
||||
"StyleCop.CSharp.NamingRules",
|
||||
"SA1300:ElementMustBeginWithUpperCaseLetter",
|
||||
Justification = "Reviewed. Suppression is OK here.")]
|
||||
public float minWidth => 0;
|
||||
|
||||
/// <inheritdoc/>
|
||||
[System.Diagnostics.CodeAnalysis.SuppressMessage(
|
||||
"StyleCop.CSharp.NamingRules",
|
||||
"SA1300:ElementMustBeginWithUpperCaseLetter",
|
||||
Justification = "Reviewed. Suppression is OK here.")]
|
||||
public float preferredHeight
|
||||
{
|
||||
get
|
||||
{
|
||||
var image = this.graphic as Image;
|
||||
if (image == null)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
switch (this.mirrorMode)
|
||||
{
|
||||
case MirrorModeType.Vertical:
|
||||
case MirrorModeType.Quarter:
|
||||
return 2 * image.preferredHeight;
|
||||
default:
|
||||
return image.preferredHeight;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
[System.Diagnostics.CodeAnalysis.SuppressMessage(
|
||||
"StyleCop.CSharp.NamingRules",
|
||||
"SA1300:ElementMustBeginWithUpperCaseLetter",
|
||||
Justification = "Reviewed. Suppression is OK here.")]
|
||||
public float preferredWidth
|
||||
{
|
||||
get
|
||||
{
|
||||
var image = this.graphic as Image;
|
||||
if (image == null)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
switch (this.mirrorMode)
|
||||
{
|
||||
case MirrorModeType.Horizontal:
|
||||
case MirrorModeType.Quarter:
|
||||
return 2 * image.preferredWidth;
|
||||
default:
|
||||
return image.preferredWidth;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Set the native size for this mirror.
|
||||
/// </summary>
|
||||
public void SetNativeSize()
|
||||
{
|
||||
var image = this.graphic as Image;
|
||||
if (image == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var overrideSprite = image.overrideSprite;
|
||||
if (overrideSprite == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var w = overrideSprite.rect.width / image.pixelsPerUnit;
|
||||
var h = overrideSprite.rect.height / image.pixelsPerUnit;
|
||||
var rectTransform = this.transform as RectTransform;
|
||||
rectTransform.anchorMax = rectTransform.anchorMin;
|
||||
|
||||
switch (this.mirrorMode)
|
||||
{
|
||||
case MirrorModeType.Horizontal:
|
||||
rectTransform.sizeDelta = new Vector2(w * 2, h);
|
||||
break;
|
||||
case MirrorModeType.Vertical:
|
||||
rectTransform.sizeDelta = new Vector2(w, h * 2);
|
||||
break;
|
||||
case MirrorModeType.Quarter:
|
||||
rectTransform.sizeDelta = new Vector2(w * 2, h * 2);
|
||||
break;
|
||||
}
|
||||
|
||||
image.SetVerticesDirty();
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public void CalculateLayoutInputHorizontal()
|
||||
{
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public void CalculateLayoutInputVertical()
|
||||
{
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override void ModifyMesh(VertexHelper vh)
|
||||
{
|
||||
if (!this.IsActive() || vh.currentVertCount <= 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var verts = ListPool<UIVertex>.Get();
|
||||
vh.GetUIVertexStream(verts);
|
||||
var newVerts = this.Modify(verts);
|
||||
if (newVerts != null)
|
||||
{
|
||||
vh.Clear();
|
||||
vh.AddUIVertexTriangleStream(newVerts);
|
||||
}
|
||||
|
||||
ListPool<UIVertex>.Release(verts);
|
||||
}
|
||||
|
||||
#if UNITY_EDITOR
|
||||
/// <inheritdoc/>
|
||||
protected override void OnValidate()
|
||||
{
|
||||
base.OnValidate();
|
||||
this.graphic?.SetVerticesDirty();
|
||||
}
|
||||
#endif
|
||||
|
||||
/// <inheritdoc/>
|
||||
protected override void OnEnable()
|
||||
{
|
||||
base.OnEnable();
|
||||
this.graphic?.SetVerticesDirty();
|
||||
}
|
||||
|
||||
private static float GetCenter(float p1, float p2, float p3)
|
||||
{
|
||||
var max = Mathf.Max(Mathf.Max(p1, p2), p3);
|
||||
var min = Mathf.Min(Mathf.Min(p1, p2), p3);
|
||||
return (max + min) / 2;
|
||||
}
|
||||
|
||||
private static Vector2 GetOverturnUV(
|
||||
Vector2 uv, float start, float end, bool isHorizontal = true)
|
||||
{
|
||||
if (isHorizontal)
|
||||
{
|
||||
uv.x = end - uv.x + start;
|
||||
}
|
||||
else
|
||||
{
|
||||
uv.y = end - uv.y + start;
|
||||
}
|
||||
|
||||
return uv;
|
||||
}
|
||||
|
||||
private List<UIVertex> Modify(List<UIVertex> verts)
|
||||
{
|
||||
var image = this.graphic as Image;
|
||||
if (image == null)
|
||||
{
|
||||
return verts;
|
||||
}
|
||||
|
||||
switch (image.type)
|
||||
{
|
||||
case Image.Type.Simple:
|
||||
return this.ModifySimple(verts);
|
||||
case Image.Type.Sliced:
|
||||
return this.ModifySliced(image, verts);
|
||||
case Image.Type.Tiled:
|
||||
return this.ModifyTiled(image, verts);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private List<UIVertex> ModifySimple(List<UIVertex> verts)
|
||||
{
|
||||
switch (this.mirrorMode)
|
||||
{
|
||||
case MirrorModeType.Horizontal:
|
||||
return this.ModifySimpleHorizontal(verts, false);
|
||||
case MirrorModeType.HorizontalInverse:
|
||||
return this.ModifySimpleHorizontal(verts, true);
|
||||
case MirrorModeType.Vertical:
|
||||
return this.ModifySimpleVertical(verts, false);
|
||||
case MirrorModeType.VerticalInverse:
|
||||
return this.ModifySimpleVertical(verts, true);
|
||||
case MirrorModeType.Quarter:
|
||||
return this.ModifySimpleQuarter(verts);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private List<UIVertex> ModifySimpleHorizontal(
|
||||
List<UIVertex> verts, bool inverse)
|
||||
{
|
||||
var rect = this.graphic.GetPixelAdjustedRect();
|
||||
var count = verts.Count;
|
||||
for (int i = 0; i < count; ++i)
|
||||
{
|
||||
var vertex = verts[i];
|
||||
|
||||
var position = vertex.position;
|
||||
position.x = (position.x + rect.x) * 0.5f;
|
||||
vertex.position = position;
|
||||
|
||||
verts[i] = vertex;
|
||||
}
|
||||
|
||||
verts.Capacity = count * 2;
|
||||
if (inverse)
|
||||
{
|
||||
this.MirrorVertsHorizontalInverse(rect, verts);
|
||||
}
|
||||
else
|
||||
{
|
||||
this.MirrorVertsHorizontal(rect, verts);
|
||||
}
|
||||
|
||||
return verts;
|
||||
}
|
||||
|
||||
private List<UIVertex> ModifySimpleVertical(
|
||||
List<UIVertex> verts, bool inverse)
|
||||
{
|
||||
var rect = this.graphic.GetPixelAdjustedRect();
|
||||
var halfHeight = rect.height * 0.5f;
|
||||
var count = verts.Count;
|
||||
for (int i = 0; i < count; ++i)
|
||||
{
|
||||
var vertex = verts[i];
|
||||
|
||||
var position = vertex.position;
|
||||
position.y = ((position.y + rect.y) * 0.5f) + halfHeight;
|
||||
vertex.position = position;
|
||||
|
||||
verts[i] = vertex;
|
||||
}
|
||||
|
||||
verts.Capacity = count * 2;
|
||||
if (inverse)
|
||||
{
|
||||
this.MirrorVertsVecticalInverse(rect, verts);
|
||||
}
|
||||
else
|
||||
{
|
||||
this.MirrorVertsVectical(rect, verts);
|
||||
}
|
||||
|
||||
return verts;
|
||||
}
|
||||
|
||||
private List<UIVertex> ModifySimpleQuarter(List<UIVertex> verts)
|
||||
{
|
||||
var rect = this.graphic.GetPixelAdjustedRect();
|
||||
var halfHeight = rect.height * 0.5f;
|
||||
var count = verts.Count;
|
||||
for (int i = 0; i < count; ++i)
|
||||
{
|
||||
var vertex = verts[i];
|
||||
|
||||
var position = vertex.position;
|
||||
position.x = (position.x + rect.x) * 0.5f;
|
||||
position.y = ((position.y + rect.y) * 0.5f) + halfHeight;
|
||||
vertex.position = position;
|
||||
|
||||
verts[i] = vertex;
|
||||
}
|
||||
|
||||
verts.Capacity = count * 4;
|
||||
this.MirrorVertsHorizontal(rect, verts);
|
||||
this.MirrorVertsVectical(rect, verts);
|
||||
return verts;
|
||||
}
|
||||
|
||||
private List<UIVertex> ModifySliced(Image image, List<UIVertex> verts)
|
||||
{
|
||||
switch (this.mirrorMode)
|
||||
{
|
||||
case MirrorModeType.Horizontal:
|
||||
return this.ModifySlicedHorizontal(image, verts, false);
|
||||
case MirrorModeType.HorizontalInverse:
|
||||
return this.ModifySlicedHorizontal(image, verts, true);
|
||||
case MirrorModeType.Vertical:
|
||||
return this.ModifySlicedVertical(image, verts, false);
|
||||
case MirrorModeType.VerticalInverse:
|
||||
return this.ModifySlicedVertical(image, verts, true);
|
||||
case MirrorModeType.Quarter:
|
||||
return this.ModifySlicedQuarter(image, verts);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private List<UIVertex> ModifySlicedHorizontal(
|
||||
Image image, List<UIVertex> verts, bool inverse)
|
||||
{
|
||||
var rect = this.graphic.GetPixelAdjustedRect();
|
||||
var halfWidth = rect.width * 0.5f;
|
||||
var midX = rect.x + halfWidth;
|
||||
var count = verts.Count;
|
||||
for (int i = 0; i < count; ++i)
|
||||
{
|
||||
var vertex = verts[i];
|
||||
var position = vertex.position;
|
||||
|
||||
if (position.x >= midX)
|
||||
{
|
||||
position.x = midX;
|
||||
}
|
||||
|
||||
vertex.position = position;
|
||||
verts[i] = vertex;
|
||||
}
|
||||
|
||||
this.SliceExcludeVerts(verts);
|
||||
verts.Capacity = count * 2;
|
||||
|
||||
if (inverse)
|
||||
{
|
||||
this.MirrorVertsHorizontalInverse(rect, verts);
|
||||
}
|
||||
else
|
||||
{
|
||||
this.MirrorVertsHorizontal(rect, verts);
|
||||
}
|
||||
|
||||
return verts;
|
||||
}
|
||||
|
||||
private List<UIVertex> ModifySlicedVertical(
|
||||
Image image, List<UIVertex> verts, bool inverse)
|
||||
{
|
||||
var rect = this.graphic.GetPixelAdjustedRect();
|
||||
var halfHeight = rect.height * 0.5f;
|
||||
var midY = rect.y + halfHeight;
|
||||
var count = verts.Count;
|
||||
for (int i = 0; i < count; ++i)
|
||||
{
|
||||
var vertex = verts[i];
|
||||
var position = vertex.position;
|
||||
|
||||
if (position.y <= midY)
|
||||
{
|
||||
position.y = midY;
|
||||
}
|
||||
|
||||
vertex.position = position;
|
||||
verts[i] = vertex;
|
||||
}
|
||||
|
||||
this.SliceExcludeVerts(verts);
|
||||
verts.Capacity = count * 2;
|
||||
if (inverse)
|
||||
{
|
||||
this.MirrorVertsVecticalInverse(rect, verts);
|
||||
}
|
||||
else
|
||||
{
|
||||
this.MirrorVertsVectical(rect, verts);
|
||||
}
|
||||
|
||||
return verts;
|
||||
}
|
||||
|
||||
private List<UIVertex> ModifySlicedQuarter(
|
||||
Image image, List<UIVertex> verts)
|
||||
{
|
||||
var rect = this.graphic.GetPixelAdjustedRect();
|
||||
var halfWidth = rect.width * 0.5f;
|
||||
var halfHeight = rect.height * 0.5f;
|
||||
var midX = rect.x + halfWidth;
|
||||
var midY = rect.y + halfHeight;
|
||||
var count = verts.Count;
|
||||
for (int i = 0; i < count; ++i)
|
||||
{
|
||||
var vertex = verts[i];
|
||||
var position = vertex.position;
|
||||
|
||||
if (position.x >= midX)
|
||||
{
|
||||
position.x = midX;
|
||||
}
|
||||
|
||||
if (position.y <= midY)
|
||||
{
|
||||
position.y = midY;
|
||||
}
|
||||
|
||||
vertex.position = position;
|
||||
verts[i] = vertex;
|
||||
}
|
||||
|
||||
this.SliceExcludeVerts(verts);
|
||||
verts.Capacity = count * 2;
|
||||
this.MirrorVertsHorizontal(rect, verts);
|
||||
this.MirrorVertsVectical(rect, verts);
|
||||
|
||||
return verts;
|
||||
}
|
||||
|
||||
private List<UIVertex> ModifyTiled(Image image, List<UIVertex> verts)
|
||||
{
|
||||
switch (this.mirrorMode)
|
||||
{
|
||||
case MirrorModeType.Horizontal:
|
||||
return this.ModifyTiledHorizontal(image, verts);
|
||||
case MirrorModeType.Vertical:
|
||||
return this.ModifyTiledVertical(image, verts);
|
||||
case MirrorModeType.Quarter:
|
||||
return this.ModifyTiledQuarter(image, verts);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private List<UIVertex> ModifyTiledHorizontal(
|
||||
Image image, List<UIVertex> verts)
|
||||
{
|
||||
var overrideSprite = image.overrideSprite;
|
||||
if (overrideSprite == null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
var rect = this.graphic.GetPixelAdjustedRect();
|
||||
var inner = DataUtility.GetInnerUV(overrideSprite);
|
||||
|
||||
var w = overrideSprite.rect.width / image.pixelsPerUnit;
|
||||
|
||||
var count = verts.Count;
|
||||
var len = count / 3;
|
||||
for (var i = 0; i < len; ++i)
|
||||
{
|
||||
var baseIndex = i * 3;
|
||||
var v1 = verts[baseIndex + 0];
|
||||
var v2 = verts[baseIndex + 1];
|
||||
var v3 = verts[baseIndex + 2];
|
||||
|
||||
var centerX = GetCenter(
|
||||
v1.position.x, v2.position.x, v3.position.x);
|
||||
if (Mathf.FloorToInt((centerX - rect.xMin) / w) % 2 == 1)
|
||||
{
|
||||
v1.uv0.x = inner.z - v1.uv0.x + inner.x;
|
||||
v2.uv0.x = inner.z - v2.uv0.x + inner.x;
|
||||
v3.uv0.x = inner.z - v3.uv0.x + inner.x;
|
||||
}
|
||||
|
||||
verts[baseIndex + 0] = v1;
|
||||
verts[baseIndex + 1] = v2;
|
||||
verts[baseIndex + 2] = v3;
|
||||
}
|
||||
|
||||
return verts;
|
||||
}
|
||||
|
||||
private List<UIVertex> ModifyTiledVertical(
|
||||
Image image, List<UIVertex> verts)
|
||||
{
|
||||
var overrideSprite = image.overrideSprite;
|
||||
if (overrideSprite == null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
var rect = this.graphic.GetPixelAdjustedRect();
|
||||
var inner = DataUtility.GetInnerUV(overrideSprite);
|
||||
|
||||
var h = overrideSprite.rect.height / image.pixelsPerUnit;
|
||||
|
||||
var count = verts.Count;
|
||||
var len = count / 3;
|
||||
for (var i = 0; i < len; ++i)
|
||||
{
|
||||
var baseIndex = i * 3;
|
||||
var v1 = verts[baseIndex + 0];
|
||||
var v2 = verts[baseIndex + 1];
|
||||
var v3 = verts[baseIndex + 2];
|
||||
|
||||
var centerY = GetCenter(v1.position.y, v2.position.y, v3.position.y);
|
||||
if (Mathf.FloorToInt((centerY - rect.yMin) / h) % 2 == 1)
|
||||
{
|
||||
v1.uv0.y = inner.w - v1.uv0.y + inner.y;
|
||||
v2.uv0.y = inner.w - v2.uv0.y + inner.y;
|
||||
v3.uv0.y = inner.w - v3.uv0.y + inner.y;
|
||||
}
|
||||
|
||||
verts[baseIndex + 0] = v1;
|
||||
verts[baseIndex + 1] = v2;
|
||||
verts[baseIndex + 2] = v3;
|
||||
}
|
||||
|
||||
return verts;
|
||||
}
|
||||
|
||||
private List<UIVertex> ModifyTiledQuarter(
|
||||
Image image, List<UIVertex> verts)
|
||||
{
|
||||
var overrideSprite = image.overrideSprite;
|
||||
if (overrideSprite == null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
var rect = this.graphic.GetPixelAdjustedRect();
|
||||
var inner = DataUtility.GetInnerUV(overrideSprite);
|
||||
|
||||
var w = overrideSprite.rect.width / image.pixelsPerUnit;
|
||||
var h = overrideSprite.rect.height / image.pixelsPerUnit;
|
||||
|
||||
var count = verts.Count;
|
||||
var len = count / 3;
|
||||
for (var i = 0; i < len; ++i)
|
||||
{
|
||||
var baseIndex = i * 3;
|
||||
var v1 = verts[baseIndex + 0];
|
||||
var v2 = verts[baseIndex + 1];
|
||||
var v3 = verts[baseIndex + 2];
|
||||
|
||||
var centerX = GetCenter(
|
||||
v1.position.x, v2.position.x, v3.position.x);
|
||||
var centerY = GetCenter(
|
||||
v1.position.y, v2.position.y, v3.position.y);
|
||||
|
||||
if (Mathf.FloorToInt((centerX - rect.xMin) / w) % 2 == 1)
|
||||
{
|
||||
v1.uv0.x = inner.z - v1.uv0.x + inner.x;
|
||||
v2.uv0.x = inner.z - v2.uv0.x + inner.x;
|
||||
v3.uv0.x = inner.z - v3.uv0.x + inner.x;
|
||||
}
|
||||
|
||||
if (Mathf.FloorToInt((centerY - rect.yMin) / h) % 2 == 1)
|
||||
{
|
||||
v1.uv0.y = inner.w - v1.uv0.y + inner.y;
|
||||
v2.uv0.y = inner.w - v2.uv0.y + inner.y;
|
||||
v3.uv0.y = inner.w - v3.uv0.y + inner.y;
|
||||
}
|
||||
|
||||
verts[baseIndex + 0] = v1;
|
||||
verts[baseIndex + 1] = v2;
|
||||
verts[baseIndex + 2] = v3;
|
||||
}
|
||||
|
||||
return verts;
|
||||
}
|
||||
|
||||
private void MirrorVertsHorizontal(Rect rect, List<UIVertex> verts)
|
||||
{
|
||||
var count = verts.Count;
|
||||
for (int i = 0; i < count; ++i)
|
||||
{
|
||||
var vertex = verts[i];
|
||||
|
||||
var position = vertex.position;
|
||||
position.x = (rect.center.x * 2) - position.x;
|
||||
vertex.position = position;
|
||||
|
||||
verts.Add(vertex);
|
||||
}
|
||||
}
|
||||
|
||||
private void MirrorVertsHorizontalInverse(
|
||||
Rect rect, List<UIVertex> verts)
|
||||
{
|
||||
var count = verts.Count;
|
||||
var center = rect.center;
|
||||
for (int i = 0; i < count; ++i)
|
||||
{
|
||||
var vertex = verts[i];
|
||||
|
||||
var position = vertex.position;
|
||||
position.x = (center.x * 2) - position.x;
|
||||
position.y = (center.y * 2) - position.y;
|
||||
vertex.position = position;
|
||||
|
||||
verts.Add(vertex);
|
||||
}
|
||||
}
|
||||
|
||||
private void MirrorVertsVectical(Rect rect, List<UIVertex> verts)
|
||||
{
|
||||
var count = verts.Count;
|
||||
for (int i = 0; i < count; ++i)
|
||||
{
|
||||
var vertex = verts[i];
|
||||
|
||||
var position = vertex.position;
|
||||
position.y = (rect.center.y * 2) - position.y;
|
||||
vertex.position = position;
|
||||
|
||||
verts.Add(vertex);
|
||||
}
|
||||
}
|
||||
|
||||
private void MirrorVertsVecticalInverse(
|
||||
Rect rect, List<UIVertex> verts)
|
||||
{
|
||||
var count = verts.Count;
|
||||
for (int i = 0; i < count; ++i)
|
||||
{
|
||||
var vertex = verts[i];
|
||||
|
||||
var position = vertex.position;
|
||||
position.x = (rect.center.x * 2) - position.x;
|
||||
position.y = (rect.center.y * 2) - position.y;
|
||||
vertex.position = position;
|
||||
|
||||
verts.Add(vertex);
|
||||
}
|
||||
}
|
||||
|
||||
private Vector4 GetAdjustedBorders(Image image, Rect rect)
|
||||
{
|
||||
var overrideSprite = image.overrideSprite;
|
||||
var border = overrideSprite.border;
|
||||
border = border / image.pixelsPerUnit;
|
||||
|
||||
var combinedWidth = border.x + border.z;
|
||||
if (rect.size.x < combinedWidth && combinedWidth != 0)
|
||||
{
|
||||
var scaleRatio = rect.size.x / combinedWidth;
|
||||
border.x *= scaleRatio;
|
||||
border.z *= scaleRatio;
|
||||
}
|
||||
|
||||
var combinedHeight = border.y + border.w;
|
||||
if (rect.size.y < combinedHeight && combinedHeight != 0)
|
||||
{
|
||||
var scaleRatio = rect.size.y / combinedHeight;
|
||||
border.y *= scaleRatio;
|
||||
border.w *= scaleRatio;
|
||||
}
|
||||
|
||||
return border;
|
||||
}
|
||||
|
||||
private void SliceExcludeVerts(List<UIVertex> verts)
|
||||
{
|
||||
var realCount = verts.Count;
|
||||
var i = 0;
|
||||
while (i < realCount)
|
||||
{
|
||||
var v1 = verts[i];
|
||||
var v2 = verts[i + 1];
|
||||
var v3 = verts[i + 2];
|
||||
|
||||
if (v1.position == v2.position ||
|
||||
v2.position == v3.position ||
|
||||
v3.position == v1.position)
|
||||
{
|
||||
verts[i] = verts[realCount - 3];
|
||||
verts[i + 1] = verts[realCount - 2];
|
||||
verts[i + 2] = verts[realCount - 1];
|
||||
|
||||
realCount -= 3;
|
||||
continue;
|
||||
}
|
||||
|
||||
i += 3;
|
||||
}
|
||||
|
||||
if (realCount < verts.Count)
|
||||
{
|
||||
verts.RemoveRange(realCount, verts.Count - realCount);
|
||||
}
|
||||
}
|
||||
}
|
||||
11
Assets/Scripts/UIExtend/ImageMirror.cs.meta
Normal file
11
Assets/Scripts/UIExtend/ImageMirror.cs.meta
Normal file
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 7f5b823c1ac5824428ff87bdfbc0d98a
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
72
Assets/Scripts/UIExtend/LongPressOrClickEventTrigger.cs
Normal file
72
Assets/Scripts/UIExtend/LongPressOrClickEventTrigger.cs
Normal file
@@ -0,0 +1,72 @@
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
using UnityEngine.Events;
|
||||
using UnityEngine.EventSystems;
|
||||
|
||||
public class LongPressOrClickEventTrigger : UIBehaviour, IPointerDownHandler, IPointerUpHandler, IPointerExitHandler,
|
||||
IPointerClickHandler
|
||||
{
|
||||
public float durationThreshold = 1.0f;
|
||||
|
||||
public UnityEvent onLongPress = new UnityEvent();
|
||||
public UnityEvent onClick = new UnityEvent();
|
||||
public UnityEvent onDown = new UnityEvent();
|
||||
public UnityEvent onUp = new UnityEvent();
|
||||
|
||||
private bool isPointerDown = false;
|
||||
private bool longPressTriggered = false;
|
||||
private float timePressStarted;
|
||||
|
||||
private void Update()
|
||||
{
|
||||
if (isPointerDown && !longPressTriggered)
|
||||
{
|
||||
if (Time.time - timePressStarted > 0.2f)
|
||||
{
|
||||
onLongPress.Invoke();
|
||||
longPressTriggered = true;
|
||||
}
|
||||
|
||||
// if (Time.time - timePressStarted > 0.2)
|
||||
// {
|
||||
// longPressTriggered = true;
|
||||
// onLongPress.Invoke();
|
||||
// }
|
||||
}
|
||||
}
|
||||
|
||||
public void OnPointerDown(PointerEventData eventData)
|
||||
{
|
||||
timePressStarted = Time.time;
|
||||
isPointerDown = true;
|
||||
longPressTriggered = false;
|
||||
onDown.Invoke();
|
||||
}
|
||||
|
||||
public void OnPointerUp(PointerEventData eventData)
|
||||
{
|
||||
isPointerDown = false;
|
||||
onUp.Invoke();
|
||||
}
|
||||
|
||||
public void OnPointerExit(PointerEventData eventData)
|
||||
{
|
||||
isPointerDown = false;
|
||||
// onUp.Invoke();
|
||||
}
|
||||
|
||||
public void OnPointerClick(PointerEventData eventData)
|
||||
{
|
||||
if (!longPressTriggered)
|
||||
{
|
||||
onClick.Invoke();
|
||||
}
|
||||
}
|
||||
protected override void OnDisable()
|
||||
{
|
||||
base.OnDisable();
|
||||
isPointerDown = false;
|
||||
longPressTriggered = false;
|
||||
}
|
||||
}
|
||||
11
Assets/Scripts/UIExtend/LongPressOrClickEventTrigger.cs.meta
Normal file
11
Assets/Scripts/UIExtend/LongPressOrClickEventTrigger.cs.meta
Normal file
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: fce04e7aebd477444a03f7700681bd6f
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
132
Assets/Scripts/UIExtend/MeshImage.cs
Normal file
132
Assets/Scripts/UIExtend/MeshImage.cs
Normal file
@@ -0,0 +1,132 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
using UnityEngine.UI;
|
||||
|
||||
public class MeshImage : Image
|
||||
{
|
||||
private Vector2[] pos;
|
||||
private Vector2[] uvs;
|
||||
|
||||
public int rotatePointIndex;
|
||||
[SerializeField]
|
||||
private bool isErrorReset;
|
||||
|
||||
public void SetPos(Vector2[] pos, bool referesh)
|
||||
{
|
||||
this.pos = pos;
|
||||
this.uvs = SetUvs(pos);
|
||||
if (referesh)
|
||||
{
|
||||
SetAllDirty();
|
||||
}
|
||||
}
|
||||
|
||||
public void SetSize(float width, float height, bool adaptPointToSize)
|
||||
{
|
||||
rectTransform.sizeDelta = new Vector2(width, height);
|
||||
if (adaptPointToSize)
|
||||
{
|
||||
float minW = 9999;
|
||||
float minH = 9999;
|
||||
float maxW = -1;
|
||||
float maxH = -1;
|
||||
foreach (var po in pos)
|
||||
{
|
||||
if (po.x < minW)
|
||||
{
|
||||
minW = po.x;
|
||||
}
|
||||
|
||||
if (po.x > maxW)
|
||||
{
|
||||
maxW = po.x;
|
||||
}
|
||||
|
||||
if (po.y < minH)
|
||||
{
|
||||
minH = po.y;
|
||||
}
|
||||
|
||||
if (po.y > maxH)
|
||||
{
|
||||
maxH = po.y;
|
||||
}
|
||||
}
|
||||
|
||||
float w = maxW - minW;
|
||||
float h = maxH - minH;
|
||||
|
||||
float sw = w / width;
|
||||
float sh = h / height;
|
||||
|
||||
float s = sw > sh ? sw : sh;
|
||||
for (int i = 0; i < pos.Length; i++)
|
||||
{
|
||||
pos[i] /= s;
|
||||
}
|
||||
|
||||
SetPos(pos, true);
|
||||
}
|
||||
}
|
||||
|
||||
public void SetRotatePointIndex(int index)
|
||||
{
|
||||
rotatePointIndex = index;
|
||||
}
|
||||
|
||||
|
||||
public Vector2[] GetPos()
|
||||
{
|
||||
return pos;
|
||||
}
|
||||
|
||||
private Vector2[] SetUvs(Vector2[] tempPos)
|
||||
{
|
||||
Vector2[] tempUvs = new Vector2[tempPos.Length];
|
||||
for (int i = 0; i < tempPos.Length; i++)
|
||||
{
|
||||
tempUvs[i] = new Vector2(tempPos[i].x / rectTransform.rect.width + 0.5f,
|
||||
tempPos[i].y / rectTransform.rect.height + 0.5f);
|
||||
}
|
||||
|
||||
return tempUvs;
|
||||
}
|
||||
|
||||
protected override void OnPopulateMesh(VertexHelper vh)
|
||||
{
|
||||
Vector2[] tempPos = pos;
|
||||
Vector2[] tempUvs = uvs;
|
||||
if (tempPos == null)
|
||||
{
|
||||
base.OnPopulateMesh(vh);
|
||||
return;
|
||||
}
|
||||
|
||||
Color32 color32 = color;
|
||||
vh.Clear();
|
||||
|
||||
try
|
||||
{
|
||||
for (int i = 0; i < tempPos.Length; i++)
|
||||
{
|
||||
vh.AddVert(tempPos[i], color32, tempUvs[i]);
|
||||
}
|
||||
|
||||
|
||||
|
||||
Triangulator triangular = new Triangulator(tempPos); //计算三角面的类
|
||||
int[] indices = triangular.Triangulate(); //得到三角面
|
||||
|
||||
for (int i = 0; i < indices.Length - 2; i += 3)
|
||||
{
|
||||
vh.AddTriangle(indices[i], indices[i + 1], indices[i + 2]);
|
||||
}
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Debug.LogError(e.ToString());
|
||||
base.OnPopulateMesh(vh);
|
||||
}
|
||||
}
|
||||
}
|
||||
11
Assets/Scripts/UIExtend/MeshImage.cs.meta
Normal file
11
Assets/Scripts/UIExtend/MeshImage.cs.meta
Normal file
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 2a37f4f91bafa7b4aaf1929f6e103d75
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
687
Assets/Scripts/UIExtend/PanelScroll.cs
Normal file
687
Assets/Scripts/UIExtend/PanelScroll.cs
Normal file
@@ -0,0 +1,687 @@
|
||||
using UnityEngine;
|
||||
using UnityEngine.UI;
|
||||
using UnityEngine.EventSystems;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using DG.Tweening;
|
||||
|
||||
public class PanelScroll : ScrollRect
|
||||
{
|
||||
// [SerializeField] public float inertiaMaxTime = 0.5f; //限制惯性持续时间
|
||||
// private float _scrolledTime = 0f;
|
||||
private Action _stopScrollCallback = null;
|
||||
private Vector2 _lastPosition = Vector2.zero;
|
||||
|
||||
private float offsetX;
|
||||
private RectTransform _content;
|
||||
private Action StopScrollCallback;
|
||||
private Action RefreshItem;
|
||||
public bool loop = false;
|
||||
|
||||
public int currentIndex;
|
||||
public int FirstIndex;
|
||||
public int CurDownIndex;
|
||||
private int minShowItemNum;
|
||||
|
||||
List<GameObject> itemGoList = new List<GameObject>();
|
||||
Vector2 startDeltaPos;
|
||||
Vector2 endDeltaDirection;
|
||||
bool isDrag = false;
|
||||
public bool isReversal = false;
|
||||
int minShowAdd = 2;
|
||||
Vector2 InverseTransformPointOffset;
|
||||
float startAdd = 0;
|
||||
|
||||
protected override void Start()
|
||||
{
|
||||
base.Start();
|
||||
RectTransform rectTransform = GetComponent<RectTransform>();
|
||||
var pivot = rectTransform.pivot - new Vector2(0.5f, 0.5f);
|
||||
var rect = rectTransform.rect.size;
|
||||
InverseTransformPointOffset = new Vector2(pivot.x * rect.x, pivot.y * rect.y);
|
||||
}
|
||||
public List<T> Init<T, T1>(float _offsetX, GameObject _item, Action<T> _OnBtnItem,
|
||||
List<T1> _ItemData, bool click = false, int _currentIndex = 0, int _minShowAdd = 2)
|
||||
where T : PanelItemBase<T1>
|
||||
{
|
||||
float spacing = 0;
|
||||
float addSizeDelta = 0; startAdd = 0;
|
||||
if (horizontal)
|
||||
{
|
||||
HorizontalLayoutGroup layoutGroup = content.GetComponent<HorizontalLayoutGroup>();
|
||||
if (layoutGroup != null)
|
||||
{
|
||||
spacing = layoutGroup.spacing;
|
||||
addSizeDelta = layoutGroup.padding.horizontal;
|
||||
startAdd = layoutGroup.padding.left;
|
||||
layoutGroup.enabled = false;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
VerticalLayoutGroup layoutGroup = content.GetComponent<VerticalLayoutGroup>();
|
||||
if (layoutGroup != null)
|
||||
{
|
||||
spacing = layoutGroup.spacing;
|
||||
addSizeDelta = layoutGroup.padding.vertical;
|
||||
startAdd = layoutGroup.padding.top;
|
||||
layoutGroup.enabled = false;
|
||||
}
|
||||
}
|
||||
_offsetX += spacing;
|
||||
minShowAdd = _minShowAdd;
|
||||
if (_currentIndex >= _ItemData.Count)
|
||||
{
|
||||
_currentIndex = _ItemData.Count - 1;
|
||||
}
|
||||
int _showItemNum;
|
||||
int showDelta = _ItemData.Count;
|
||||
if (horizontal)
|
||||
minShowItemNum = Mathf.CeilToInt(GetComponent<RectTransform>().rect.width / _offsetX) + minShowAdd;
|
||||
else
|
||||
minShowItemNum = Mathf.CeilToInt(GetComponent<RectTransform>().rect.height / _offsetX) + minShowAdd;
|
||||
_showItemNum = minShowItemNum;
|
||||
if (loop)
|
||||
{
|
||||
showDelta = minShowItemNum;
|
||||
}
|
||||
else if (minShowItemNum > _ItemData.Count)
|
||||
{
|
||||
_showItemNum = showDelta;
|
||||
}
|
||||
movementType = loop ? MovementType.Unrestricted : MovementType.Elastic;
|
||||
offsetX = _offsetX;
|
||||
_item.gameObject.SetActive(false);
|
||||
_content = transform.Find("Viewport/Content").GetComponent<RectTransform>();
|
||||
_content.sizeDelta = horizontal ? new Vector2(showDelta * _offsetX + addSizeDelta, _content.sizeDelta.y) : new Vector2(_content.sizeDelta.x, showDelta * _offsetX + addSizeDelta);
|
||||
if (isReversal)
|
||||
{
|
||||
_content.anchoredPosition = horizontal ?
|
||||
new Vector2(loop ? -((_showItemNum - 1) / 2f - _currentIndex) * _offsetX : _currentIndex * _offsetX, _content.anchoredPosition.y) :
|
||||
new Vector2(_content.anchoredPosition.x, loop ? -(_currentIndex - (_showItemNum - 1) / 2f) * _offsetX : -_currentIndex * _offsetX);
|
||||
}
|
||||
else
|
||||
{
|
||||
_content.anchoredPosition = horizontal ?
|
||||
new Vector2(loop ? ((_showItemNum - 1) / 2f - _currentIndex) * _offsetX : -_currentIndex * _offsetX, _content.anchoredPosition.y) :
|
||||
new Vector2(_content.anchoredPosition.x, loop ? (_currentIndex - (_showItemNum - 1) / 2f) * _offsetX : _currentIndex * _offsetX);
|
||||
}
|
||||
List<T> items = new List<T>();
|
||||
|
||||
int count = itemGoList.Count;
|
||||
for (int i = 0; i < count; i++)
|
||||
{
|
||||
itemGoList[i].SetActive(false);
|
||||
}
|
||||
for (int i = 0; i < _showItemNum; i++)
|
||||
{
|
||||
GameObject go;
|
||||
if (count > i)
|
||||
{
|
||||
go = itemGoList[i].gameObject;
|
||||
}
|
||||
else
|
||||
{
|
||||
go = Instantiate(_item, _content);
|
||||
itemGoList.Add(go);
|
||||
}
|
||||
go.SetActive(true);
|
||||
if (isReversal)
|
||||
{
|
||||
go.transform.GetComponent<RectTransform>().anchoredPosition = horizontal ?
|
||||
new Vector3(-(i + 0.5f) * _offsetX - startAdd, go.transform.GetComponent<RectTransform>().anchoredPosition.y, 0) :
|
||||
new Vector3(go.transform.GetComponent<RectTransform>().anchoredPosition.x, (i + 0.5f) * _offsetX + startAdd, 0);
|
||||
}
|
||||
else
|
||||
{
|
||||
go.transform.GetComponent<RectTransform>().anchoredPosition = horizontal ?
|
||||
new Vector3((i + 0.5f) * _offsetX + startAdd, go.transform.GetComponent<RectTransform>().anchoredPosition.y, 0) :
|
||||
new Vector3(go.transform.GetComponent<RectTransform>().anchoredPosition.x, -(i + 0.5f) * _offsetX - startAdd, 0);
|
||||
}
|
||||
T t = go.GetComponent<T>() ?? go.AddComponent<T>();
|
||||
items.Add(t);
|
||||
items[i].index = i;
|
||||
var i1 = i;
|
||||
items[i].OnBtnItemAction = () => { _OnBtnItem?.Invoke(items[i1]); };
|
||||
items[i].SetDataAction = () =>
|
||||
{
|
||||
int count = _ItemData.Count;
|
||||
items[i1].index %= count;
|
||||
if (items[i1].index < 0)
|
||||
{
|
||||
items[i1].index += count;
|
||||
}
|
||||
|
||||
return _ItemData[items[i1].index];
|
||||
};
|
||||
}
|
||||
if (isReversal)
|
||||
{
|
||||
ReversalSetOnValueChanged(items, _ItemData);
|
||||
}
|
||||
else
|
||||
{
|
||||
SetOnValueChanged(items, _ItemData);
|
||||
}
|
||||
SetStopScrollCallback(click, items, _ItemData);
|
||||
RefreshItem = () =>
|
||||
{
|
||||
foreach (var t in items)
|
||||
{
|
||||
t.SetData();
|
||||
}
|
||||
};
|
||||
currentIndex = _currentIndex;
|
||||
if (currentIndex > 1)
|
||||
{
|
||||
if (!loop)
|
||||
{
|
||||
if (currentIndex + _showItemNum > showDelta)
|
||||
{
|
||||
currentIndex = showDelta - _showItemNum;
|
||||
}
|
||||
currentIndex--;
|
||||
}
|
||||
//currentIndex -= minShowItemNum;
|
||||
for (int i = items.Count - 1; i >= 0; i--)
|
||||
{
|
||||
T t = items[i];
|
||||
if (isReversal)
|
||||
{
|
||||
t.rectTransform.anchoredPosition = horizontal ?
|
||||
new Vector3(t.rectTransform.anchoredPosition.x - currentIndex * offsetX, t.rectTransform.anchoredPosition.y, 0) :
|
||||
new Vector3(t.rectTransform.anchoredPosition.x, t.rectTransform.anchoredPosition.y + currentIndex * offsetX, 0);
|
||||
}
|
||||
else
|
||||
{
|
||||
t.rectTransform.anchoredPosition = horizontal ?
|
||||
new Vector3(t.rectTransform.anchoredPosition.x + currentIndex * offsetX, t.rectTransform.anchoredPosition.y, 0) :
|
||||
new Vector3(t.rectTransform.anchoredPosition.x, t.rectTransform.anchoredPosition.y - currentIndex * offsetX, 0);
|
||||
|
||||
}
|
||||
t.index += currentIndex;
|
||||
}
|
||||
}
|
||||
RefreshItem();
|
||||
currentIndex = _currentIndex;
|
||||
if (click)
|
||||
{
|
||||
if (currentIndex > 0)
|
||||
{
|
||||
for (int i = 0; i < items.Count; i++)
|
||||
{
|
||||
if (items[i].index == _currentIndex)
|
||||
{
|
||||
_OnBtnItem?.Invoke(items[i]);
|
||||
return items;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (items.Count > 0)
|
||||
{
|
||||
_OnBtnItem?.Invoke(items[0]);
|
||||
}
|
||||
}
|
||||
return items;
|
||||
}
|
||||
void ReversalSetOnValueChanged<T, T1>(List<T> items, List<T1> _ItemData) where T : PanelItemBase<T1>
|
||||
{
|
||||
onValueChanged.RemoveAllListeners();
|
||||
if (horizontal)
|
||||
{
|
||||
onValueChanged.AddListener(arg0 =>
|
||||
{
|
||||
foreach (var t in items)
|
||||
{
|
||||
//transform相对于scrollRect的位置
|
||||
var arg1 = InverseTransformPoint(t.transform.position);
|
||||
if (arg1.x < -(minShowItemNum + 1) / 2 * offsetX/* - offsetX*/)
|
||||
{
|
||||
if (loop || t.index - minShowItemNum >= 0)
|
||||
{
|
||||
t.rectTransform.anchoredPosition =
|
||||
new Vector3(t.rectTransform.anchoredPosition.x + minShowItemNum * offsetX, t.rectTransform.anchoredPosition.y, 0);
|
||||
t.OnChangIndex(-minShowItemNum);
|
||||
}
|
||||
}
|
||||
else if (arg1.x > (minShowItemNum + 1) / 2 * offsetX /*+ offsetX*/)
|
||||
{
|
||||
if (loop || t.index + minShowItemNum < _ItemData.Count)
|
||||
{
|
||||
t.rectTransform.anchoredPosition =
|
||||
new Vector3(t.rectTransform.anchoredPosition.x - minShowItemNum * offsetX, t.rectTransform.anchoredPosition.y, 0);
|
||||
t.OnChangIndex(minShowItemNum);
|
||||
}
|
||||
}
|
||||
arg1 = InverseTransformPoint(t.transform.position);
|
||||
t.OffsetX(arg1.x, offsetX);
|
||||
}
|
||||
});
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
onValueChanged.AddListener(arg0 =>
|
||||
{
|
||||
foreach (var t in items)
|
||||
{
|
||||
//transform相对于scrollRect的位置
|
||||
var arg1 = InverseTransformPoint(t.transform.position);
|
||||
if (arg1.y < -(minShowItemNum + 1) / 2 * offsetX/* - offsetX*/)
|
||||
{
|
||||
if (loop || t.index + minShowItemNum < _ItemData.Count)
|
||||
{
|
||||
t.rectTransform.anchoredPosition =
|
||||
new Vector3(t.rectTransform.anchoredPosition.x, t.rectTransform.anchoredPosition.y + minShowItemNum * offsetX, 0);
|
||||
t.OnChangIndex(minShowItemNum);
|
||||
}
|
||||
}
|
||||
else if (arg1.y > (minShowItemNum + 1) / 2 * offsetX /*+ offsetX*/)
|
||||
{
|
||||
if (loop || t.index - minShowItemNum >= 0)
|
||||
{
|
||||
t.rectTransform.anchoredPosition =
|
||||
new Vector3(t.rectTransform.anchoredPosition.x, t.rectTransform.anchoredPosition.y - minShowItemNum * offsetX, 0);
|
||||
t.OnChangIndex(-minShowItemNum);
|
||||
}
|
||||
}
|
||||
arg1 = InverseTransformPoint(t.transform.position);
|
||||
t.OffsetX(arg1.y, offsetX);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
void SetOnValueChanged<T, T1>(List<T> items, List<T1> _ItemData) where T : PanelItemBase<T1>
|
||||
{
|
||||
onValueChanged.RemoveAllListeners();
|
||||
if (horizontal)
|
||||
{
|
||||
onValueChanged.AddListener(arg0 =>
|
||||
{
|
||||
var panelHeight = GetComponent<RectTransform>().rect.width;
|
||||
var panelUpPos = panelHeight / 2f;
|
||||
var PanelDownPos = -panelHeight / 2f;
|
||||
foreach (var t in items)
|
||||
{
|
||||
//transform相对于scrollRect的位置
|
||||
var arg1 = InverseTransformPoint(t.transform.position);
|
||||
|
||||
//进入退出窗口判断
|
||||
var itemUpPos = arg1.x + offsetX / 2f;
|
||||
var itemDownPos = arg1.x - offsetX / 2f;
|
||||
|
||||
|
||||
if (itemUpPos > PanelDownPos && CurDownIndex < t.index)
|
||||
CurDownIndex = t.index;
|
||||
if (itemUpPos < PanelDownPos && CurDownIndex >= t.index)
|
||||
CurDownIndex = Mathf.Clamp(t.index - 1, 0, _ItemData.Count - 1);
|
||||
|
||||
if (itemDownPos < panelUpPos && FirstIndex > t.index)
|
||||
{
|
||||
FirstIndex = t.index;
|
||||
}
|
||||
if (itemDownPos > panelUpPos && FirstIndex <= t.index)
|
||||
FirstIndex = Mathf.Clamp(t.index + 1, 0, _ItemData.Count - 1);
|
||||
|
||||
|
||||
if (arg1.x < -(minShowItemNum + 1) / 2 * offsetX/* - offsetX*/)
|
||||
{
|
||||
if (loop || t.index + minShowItemNum < _ItemData.Count)
|
||||
{
|
||||
t.rectTransform.anchoredPosition =
|
||||
new Vector3(t.rectTransform.anchoredPosition.x + minShowItemNum * offsetX, t.rectTransform.anchoredPosition.y, 0);
|
||||
t.OnChangIndex(minShowItemNum);
|
||||
}
|
||||
}
|
||||
else if (arg1.x > (minShowItemNum + 1) / 2 * offsetX /*+ offsetX*/)
|
||||
{
|
||||
if (loop || t.index - minShowItemNum >= 0)
|
||||
{
|
||||
t.rectTransform.anchoredPosition =
|
||||
new Vector3(t.rectTransform.anchoredPosition.x - minShowItemNum * offsetX, t.rectTransform.anchoredPosition.y, 0);
|
||||
t.OnChangIndex(-minShowItemNum);
|
||||
}
|
||||
}
|
||||
arg1 = InverseTransformPoint(t.transform.position);
|
||||
t.OffsetX(arg1.x, offsetX);
|
||||
}
|
||||
});
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
onValueChanged.AddListener(arg0 =>
|
||||
{
|
||||
var panelHeight = GetComponent<RectTransform>().rect.height;
|
||||
var panelUpPos = panelHeight / 2f;
|
||||
var PanelDownPos = -panelHeight / 2f;
|
||||
foreach (var t in items)
|
||||
{
|
||||
//transform相对于scrollRect的位置
|
||||
var arg1 = InverseTransformPoint(t.transform.position);
|
||||
|
||||
//进入退出窗口判断
|
||||
var itemUpPos = arg1.y + offsetX / 2f;
|
||||
var itemDownPos = arg1.y - offsetX / 2f;
|
||||
|
||||
|
||||
if (itemUpPos > PanelDownPos && CurDownIndex < t.index)
|
||||
CurDownIndex = t.index;
|
||||
if (itemUpPos < PanelDownPos && CurDownIndex >= t.index)
|
||||
CurDownIndex = Mathf.Clamp(t.index - 1, 0, _ItemData.Count - 1);
|
||||
|
||||
if (itemDownPos < panelUpPos && FirstIndex > t.index)
|
||||
{
|
||||
FirstIndex = t.index;
|
||||
}
|
||||
if (itemDownPos > panelUpPos && FirstIndex <= t.index)
|
||||
FirstIndex = Mathf.Clamp(t.index + 1, 0, _ItemData.Count - 1);
|
||||
|
||||
|
||||
//循环判断
|
||||
if (arg1.y < -(minShowItemNum + 1) / 2 * offsetX /*- offsetX*/)
|
||||
{
|
||||
if (loop || t.index - minShowItemNum >= 0)
|
||||
{
|
||||
t.rectTransform.anchoredPosition =
|
||||
new Vector3(t.rectTransform.anchoredPosition.x, t.rectTransform.anchoredPosition.y + minShowItemNum * offsetX, 0);
|
||||
t.OnChangIndex(-minShowItemNum);
|
||||
}
|
||||
}
|
||||
else if (arg1.y > (minShowItemNum + 1) / 2 * offsetX /*+ offsetX*/)
|
||||
{
|
||||
if (loop || t.index + minShowItemNum < _ItemData.Count)
|
||||
{
|
||||
t.rectTransform.anchoredPosition =
|
||||
new Vector3(t.rectTransform.anchoredPosition.x, t.rectTransform.anchoredPosition.y - minShowItemNum * offsetX, 0);
|
||||
t.OnChangIndex(minShowItemNum);
|
||||
}
|
||||
}
|
||||
arg1 = InverseTransformPoint(t.transform.position);
|
||||
t.OffsetX(arg1.y, offsetX);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
void SetStopScrollCallback<T, T1>(bool click, List<T> items, List<T1> _ItemData) where T : PanelItemBase<T1>
|
||||
{
|
||||
if (click)
|
||||
{
|
||||
if (horizontal)
|
||||
{
|
||||
StopScrollCallback = () =>
|
||||
{
|
||||
int count = items.Count;
|
||||
for (int i = 0; i < count; i++)
|
||||
{
|
||||
T t = items[i];
|
||||
Vector3 pos = InverseTransformPoint(t.transform.position);
|
||||
if (pos.x > -offsetX / 2 - 0.01f && pos.x < offsetX / 2 + 0.01f)
|
||||
{
|
||||
if (isDrag)
|
||||
{
|
||||
if (endDeltaDirection.x > 1)
|
||||
{
|
||||
if (pos.x > 5)
|
||||
{
|
||||
if (i != 0)
|
||||
{
|
||||
if (loop || currentIndex > 0)
|
||||
{
|
||||
t = items[i - 1];
|
||||
}
|
||||
}
|
||||
else if (loop || t.index - minShowItemNum / 2 >= 0)
|
||||
{
|
||||
t = items[^1];
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (endDeltaDirection.x < -1)
|
||||
{
|
||||
if (pos.x < -5)
|
||||
{
|
||||
if (i < count - 1)
|
||||
{
|
||||
if (loop || currentIndex < _ItemData.Count - 1)
|
||||
{
|
||||
t = items[i + 1];
|
||||
}
|
||||
}
|
||||
else if (loop || t.index + minShowItemNum / 2 < _ItemData.Count)
|
||||
{
|
||||
t = items[0];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!loop)
|
||||
{
|
||||
content.DOLocalMoveX(-t.transform.localPosition.x + 0.5f * offsetX, 0.2f);
|
||||
}
|
||||
currentIndex = t.index;
|
||||
t.OnBtnItem();
|
||||
break;
|
||||
}
|
||||
}
|
||||
isDrag = false;
|
||||
};
|
||||
}
|
||||
else
|
||||
{
|
||||
StopScrollCallback = () =>
|
||||
{
|
||||
int count = items.Count;
|
||||
for (int i = 0; i < count; i++)
|
||||
{
|
||||
T t = items[i];
|
||||
Vector3 pos = InverseTransformPoint(t.transform.position);
|
||||
if (pos.y > -offsetX / 2 - 0.01f && pos.y < offsetX / 2 + 0.01f)
|
||||
{
|
||||
if (isDrag)
|
||||
{
|
||||
if (endDeltaDirection.y > 1)
|
||||
{
|
||||
if (pos.y > 10)
|
||||
{
|
||||
if (i < count - 1)
|
||||
{
|
||||
t = items[i + 1];
|
||||
}
|
||||
else if (loop || t.index + minShowItemNum / 2 < _ItemData.Count)
|
||||
{
|
||||
t = items[0];
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (endDeltaDirection.y < -1)
|
||||
{
|
||||
if (pos.y < -10)
|
||||
{
|
||||
if (i != 0)
|
||||
{
|
||||
t = items[i - 1];
|
||||
}
|
||||
else if (loop || t.index - minShowItemNum / 2 >= 0)
|
||||
{
|
||||
t = items[^1];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!loop)
|
||||
{
|
||||
content.DOLocalMoveY(-t.transform.localPosition.y - 0.5f * offsetX, 0.2f);
|
||||
}
|
||||
|
||||
currentIndex = t.index;
|
||||
t.OnBtnItem();
|
||||
break;
|
||||
}
|
||||
}
|
||||
isDrag = false;
|
||||
};
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
StopScrollCallback = null;
|
||||
}
|
||||
}
|
||||
|
||||
//跳到目标位置
|
||||
public void JumpToTarget(int index, float posOffset = 0f)
|
||||
{
|
||||
float offset = offsetX;
|
||||
if (isReversal)
|
||||
{
|
||||
offset = -offsetX;
|
||||
}
|
||||
if (loop)
|
||||
{
|
||||
if (index > currentIndex)
|
||||
{
|
||||
if (horizontal)
|
||||
{
|
||||
_content.DOLocalMoveX(_content.localPosition.x - (index - currentIndex) * offset + posOffset, 0.3f).OnComplete(() => { StopScrollCallback?.Invoke(); });
|
||||
}
|
||||
else
|
||||
{
|
||||
_content.DOLocalMoveY(_content.localPosition.y + (index - currentIndex) * offset + posOffset, 0.3f).OnComplete(() => { StopScrollCallback?.Invoke(); });
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (horizontal)
|
||||
{
|
||||
_content.DOLocalMoveX(_content.localPosition.x + (currentIndex - index) * offset + posOffset, 0.3f).OnComplete(() => { StopScrollCallback?.Invoke(); });
|
||||
}
|
||||
else
|
||||
{
|
||||
_content.DOLocalMoveY(_content.localPosition.y - (currentIndex - index) * offset + posOffset, 0.3f).OnComplete(() => { StopScrollCallback?.Invoke(); });
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
index -= (int)((minShowItemNum - minShowAdd) / 2f);
|
||||
if (horizontal)
|
||||
{
|
||||
_content.DOLocalMoveX(-index * offset + posOffset, 0.5f).OnComplete(() => { StopScrollCallback?.Invoke(); });
|
||||
}
|
||||
else
|
||||
{
|
||||
_content.DOLocalMoveY(index * offset + posOffset, 0.5f).OnComplete(() => { StopScrollCallback?.Invoke(); });
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void Refresh()
|
||||
{
|
||||
RefreshItem?.Invoke();
|
||||
}
|
||||
public override void OnBeginDrag(PointerEventData eventData)
|
||||
{
|
||||
base.OnBeginDrag(eventData);
|
||||
startDeltaPos = eventData.position;
|
||||
}
|
||||
public override void OnEndDrag(PointerEventData eventData)
|
||||
{
|
||||
base.OnEndDrag(eventData);
|
||||
endDeltaDirection = eventData.position - startDeltaPos;
|
||||
isDrag = true;
|
||||
_stopScrollCallback = StopScrollCallback;
|
||||
// _scrolledTime = 0f;
|
||||
//如果没有在移动
|
||||
if (this.velocity.magnitude < 3)
|
||||
{
|
||||
if (_stopScrollCallback != null)
|
||||
{
|
||||
_stopScrollCallback();
|
||||
_stopScrollCallback = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public override void OnDrag(PointerEventData eventData)
|
||||
{
|
||||
base.OnDrag(eventData);
|
||||
// _scrolledTime = 0f;
|
||||
}
|
||||
|
||||
protected override void SetContentAnchoredPosition(Vector2 position)
|
||||
{
|
||||
if (Vector3.Distance(_lastPosition, position) < 3)
|
||||
{
|
||||
if (_stopScrollCallback != null)
|
||||
{
|
||||
_stopScrollCallback();
|
||||
_stopScrollCallback = null;
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
_lastPosition = position;
|
||||
base.SetContentAnchoredPosition(position);
|
||||
|
||||
// _scrolledTime += Time.unscaledDeltaTime;
|
||||
}
|
||||
public Vector3 InverseTransformPoint(Vector3 position)
|
||||
{
|
||||
var point = transform.InverseTransformPoint(position);
|
||||
point.x += InverseTransformPointOffset.x;
|
||||
point.y += InverseTransformPointOffset.y;
|
||||
return point;
|
||||
}
|
||||
}
|
||||
|
||||
public abstract class PanelItemBase<T> : MonoBehaviour
|
||||
{
|
||||
private RectTransform _rectTransform;
|
||||
|
||||
public Action OnBtnItemAction;
|
||||
public Func<T> SetDataAction;
|
||||
public T data;
|
||||
|
||||
public RectTransform rectTransform
|
||||
{
|
||||
get
|
||||
{
|
||||
if (_rectTransform == null)
|
||||
{
|
||||
_rectTransform = GetComponent<RectTransform>();
|
||||
}
|
||||
|
||||
return _rectTransform;
|
||||
}
|
||||
}
|
||||
|
||||
public int index = 0;
|
||||
|
||||
public abstract void OnInit();
|
||||
|
||||
public virtual void OffsetX(float _offsetX, float width)
|
||||
{
|
||||
}
|
||||
|
||||
public virtual void SetData()
|
||||
{
|
||||
data = SetDataAction();
|
||||
|
||||
OnInit();
|
||||
}
|
||||
|
||||
public virtual void OnBtnItem()
|
||||
{
|
||||
OnBtnItemAction?.Invoke();
|
||||
}
|
||||
|
||||
public void OnChangIndex(int _index)
|
||||
{
|
||||
index += _index;
|
||||
SetData();
|
||||
}
|
||||
}
|
||||
3
Assets/Scripts/UIExtend/PanelScroll.cs.meta
Normal file
3
Assets/Scripts/UIExtend/PanelScroll.cs.meta
Normal file
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: cb82d7ec26d44381af77c9fe5eb75c46
|
||||
timeCreated: 1690966171
|
||||
41
Assets/Scripts/UIExtend/ScreenSpaceTrail.cs
Normal file
41
Assets/Scripts/UIExtend/ScreenSpaceTrail.cs
Normal file
@@ -0,0 +1,41 @@
|
||||
using UnityEngine;
|
||||
using UnityEngine.UI;
|
||||
|
||||
[RequireComponent(typeof(RectTransform), typeof(TrailRenderer))]
|
||||
public class ScreenSpaceTrail : MonoBehaviour
|
||||
{
|
||||
private RectTransform rectTransform;
|
||||
private TrailRenderer trail;
|
||||
private Camera uiCamera;
|
||||
|
||||
void Start()
|
||||
{
|
||||
rectTransform = GetComponent<RectTransform>();
|
||||
trail = GetComponent<TrailRenderer>();
|
||||
uiCamera = GetComponentInParent<Canvas>().worldCamera;
|
||||
}
|
||||
|
||||
void Update()
|
||||
{
|
||||
UpdateTrailPosition();
|
||||
}
|
||||
|
||||
void UpdateTrailPosition()
|
||||
{
|
||||
// 将UI坐标转换为世界坐标
|
||||
Vector3 screenPos = RectTransformUtility.WorldToScreenPoint(
|
||||
uiCamera,
|
||||
rectTransform.position);
|
||||
|
||||
// 设置Z值在相机近裁剪面稍前方
|
||||
Vector3 worldPos = uiCamera.ScreenToWorldPoint(
|
||||
new Vector3(screenPos.x, screenPos.y, uiCamera.nearClipPlane + 0.1f));
|
||||
|
||||
transform.position = worldPos;
|
||||
}
|
||||
|
||||
public void ClearTrail()
|
||||
{
|
||||
trail.Clear();
|
||||
}
|
||||
}
|
||||
11
Assets/Scripts/UIExtend/ScreenSpaceTrail.cs.meta
Normal file
11
Assets/Scripts/UIExtend/ScreenSpaceTrail.cs.meta
Normal file
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 64a59ef44005a8c449362d886a44b68d
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
97
Assets/Scripts/UIExtend/SubScrollRect.cs
Normal file
97
Assets/Scripts/UIExtend/SubScrollRect.cs
Normal file
@@ -0,0 +1,97 @@
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
using UnityEngine.EventSystems;
|
||||
using UnityEngine.UI;
|
||||
|
||||
public class SubScrollRect : ScrollRect
|
||||
{
|
||||
//父ScrollRect对象
|
||||
private ScrollRect m_Parent;
|
||||
|
||||
public enum Direction
|
||||
{
|
||||
Horizontal,
|
||||
Vertical
|
||||
}
|
||||
|
||||
//滑动方向
|
||||
private Direction m_Direction = Direction.Horizontal;
|
||||
|
||||
//当前操作方向
|
||||
private Direction m_BeginDragDirection = Direction.Horizontal;
|
||||
|
||||
protected override void Start()
|
||||
{
|
||||
base.Start();
|
||||
//找到父对象
|
||||
Transform parent = transform.parent;
|
||||
m_Parent = parent.GetComponentInParent<ScrollRect>();
|
||||
|
||||
m_Direction = this.horizontal ? Direction.Horizontal : Direction.Vertical;
|
||||
}
|
||||
|
||||
|
||||
public override void OnBeginDrag(PointerEventData eventData)
|
||||
{
|
||||
if (m_Parent)
|
||||
{
|
||||
m_BeginDragDirection = Mathf.Abs(eventData.delta.x) > Mathf.Abs(eventData.delta.y)
|
||||
? Direction.Horizontal
|
||||
: Direction.Vertical;
|
||||
if (m_BeginDragDirection != m_Direction)
|
||||
{
|
||||
//当前操作方向不等于滑动方向,将事件传给父对象
|
||||
ExecuteEvents.Execute(m_Parent.gameObject, eventData, ExecuteEvents.beginDragHandler);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
base.OnBeginDrag(eventData);
|
||||
}
|
||||
|
||||
public override void OnDrag(PointerEventData eventData)
|
||||
{
|
||||
if (m_Parent)
|
||||
{
|
||||
if (m_BeginDragDirection != m_Direction)
|
||||
{
|
||||
//当前操作方向不等于滑动方向,将事件传给父对象
|
||||
ExecuteEvents.Execute(m_Parent.gameObject, eventData, ExecuteEvents.dragHandler);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
base.OnDrag(eventData);
|
||||
}
|
||||
|
||||
public override void OnEndDrag(PointerEventData eventData)
|
||||
{
|
||||
if (m_Parent)
|
||||
{
|
||||
if (m_BeginDragDirection != m_Direction)
|
||||
{
|
||||
//当前操作方向不等于滑动方向,将事件传给父对象
|
||||
ExecuteEvents.Execute(m_Parent.gameObject, eventData, ExecuteEvents.endDragHandler);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
base.OnEndDrag(eventData);
|
||||
}
|
||||
|
||||
public override void OnScroll(PointerEventData data)
|
||||
{
|
||||
if (m_Parent)
|
||||
{
|
||||
if (m_BeginDragDirection != m_Direction)
|
||||
{
|
||||
//当前操作方向不等于滑动方向,将事件传给父对象
|
||||
ExecuteEvents.Execute(m_Parent.gameObject, data, ExecuteEvents.scrollHandler);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
base.OnScroll(data);
|
||||
}
|
||||
}
|
||||
3
Assets/Scripts/UIExtend/SubScrollRect.cs.meta
Normal file
3
Assets/Scripts/UIExtend/SubScrollRect.cs.meta
Normal file
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: edfe100865c549d1ad3874cb648c5f3c
|
||||
timeCreated: 1691565987
|
||||
68
Assets/Scripts/UIExtend/TMP_Text_Curved.cs
Normal file
68
Assets/Scripts/UIExtend/TMP_Text_Curved.cs
Normal file
@@ -0,0 +1,68 @@
|
||||
using TMPro;
|
||||
using UnityEngine;
|
||||
[RequireComponent(typeof(TMP_Text))]
|
||||
public class TMP_Text_Curved : MonoBehaviour
|
||||
{
|
||||
public TMP_Text m_TextComponent;
|
||||
public float height = 1;
|
||||
public float allWidth = 800;
|
||||
public float radian_angle = 30f;
|
||||
private void OnValidate()
|
||||
{
|
||||
if (m_TextComponent == null)
|
||||
{
|
||||
m_TextComponent = GetComponent<TMP_Text>();
|
||||
}
|
||||
SetTextCurved();
|
||||
}
|
||||
private void Awake()
|
||||
{
|
||||
if (m_TextComponent == null)
|
||||
{
|
||||
m_TextComponent = GetComponent<TMP_Text>();
|
||||
}
|
||||
}
|
||||
private void LateUpdate()
|
||||
{
|
||||
SetTextCurved();
|
||||
}
|
||||
void SetTextCurved()
|
||||
{
|
||||
m_TextComponent.ForceMeshUpdate();
|
||||
TMP_TextInfo textInfo = m_TextComponent.textInfo;
|
||||
if (textInfo == null) return;
|
||||
int characterCount = textInfo.characterCount;
|
||||
if (characterCount == 0) return;
|
||||
Quaternion rotation;
|
||||
Vector3 center;
|
||||
var charInfo = textInfo.characterInfo[characterCount - 1];
|
||||
Vector3[] vertices;
|
||||
int vertexIndex;
|
||||
for (int i = 0; i < characterCount; i++)
|
||||
{
|
||||
charInfo = textInfo.characterInfo[i];
|
||||
if (!charInfo.isVisible) continue;
|
||||
vertices = textInfo.meshInfo[charInfo.materialReferenceIndex].vertices;
|
||||
vertexIndex = charInfo.vertexIndex;
|
||||
center = (vertices[vertexIndex + 0] + vertices[vertexIndex + 1] + vertices[vertexIndex + 2] + vertices[vertexIndex + 3]) / 4;
|
||||
|
||||
float angle = center.x < 0 ? vertices[vertexIndex + 0].x / allWidth : vertices[vertexIndex + 3].x / allWidth;
|
||||
rotation = Quaternion.Euler(0, 0, -angle * radian_angle);
|
||||
vertices[vertexIndex + 0] = rotation * (vertices[vertexIndex + 0] - center) + center;
|
||||
vertices[vertexIndex + 1] = rotation * (vertices[vertexIndex + 1] - center) + center;
|
||||
vertices[vertexIndex + 2] = rotation * (vertices[vertexIndex + 2] - center) + center;
|
||||
vertices[vertexIndex + 3] = rotation * (vertices[vertexIndex + 3] - center) + center;
|
||||
Vector3 offset = Vector3.up * (Mathf.Cos(angle * Mathf.PI) - 1f) * height;
|
||||
vertices[vertexIndex + 0] += offset;
|
||||
vertices[vertexIndex + 1] += offset;
|
||||
vertices[vertexIndex + 2] += offset;
|
||||
vertices[vertexIndex + 3] += offset;
|
||||
}
|
||||
for (int i = 0; i < textInfo.meshInfo.Length; i++)
|
||||
{
|
||||
var meshInfo = textInfo.meshInfo[i];
|
||||
meshInfo.mesh.vertices = meshInfo.vertices;
|
||||
m_TextComponent.UpdateGeometry(meshInfo.mesh, i);
|
||||
}
|
||||
}
|
||||
}
|
||||
11
Assets/Scripts/UIExtend/TMP_Text_Curved.cs.meta
Normal file
11
Assets/Scripts/UIExtend/TMP_Text_Curved.cs.meta
Normal file
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: e97c7089d02127542be97cbb2e963b85
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
96
Assets/Scripts/UIExtend/TextLocalization.cs
Normal file
96
Assets/Scripts/UIExtend/TextLocalization.cs
Normal file
@@ -0,0 +1,96 @@
|
||||
using asap.core;
|
||||
using System;
|
||||
using TMPro;
|
||||
using UniRx;
|
||||
using UnityEngine;
|
||||
using UnityEngine.UI;
|
||||
|
||||
namespace GameCore
|
||||
{
|
||||
public enum TextType
|
||||
{
|
||||
Text, // UGUI Text
|
||||
TextMeshPro, // TextMeshPro - Text
|
||||
TextMeshPro_UGUI // TextMeshPro - Text(UI)
|
||||
}
|
||||
|
||||
public class TextLocalization : MonoBehaviour
|
||||
{
|
||||
[SerializeField] private string localizationId;
|
||||
[SerializeField] private TextType textType = TextType.TextMeshPro_UGUI;
|
||||
|
||||
private Text text;
|
||||
private TextMeshProUGUI text_UGUI;
|
||||
private TextMeshPro text_Pro;
|
||||
private IDisposable disposable;
|
||||
|
||||
void Awake()
|
||||
{
|
||||
if (string.IsNullOrEmpty(localizationId))
|
||||
{
|
||||
return;
|
||||
}
|
||||
switch (textType)
|
||||
{
|
||||
case TextType.Text:
|
||||
text = gameObject.GetComponent<Text>();
|
||||
break;
|
||||
case TextType.TextMeshPro_UGUI:
|
||||
text_UGUI = gameObject.GetComponent<TextMeshProUGUI>();
|
||||
break;
|
||||
case TextType.TextMeshPro:
|
||||
text_Pro = gameObject.GetComponent<TextMeshPro>();
|
||||
break;
|
||||
}
|
||||
|
||||
if (string.IsNullOrEmpty(localizationId))
|
||||
{
|
||||
return;
|
||||
}
|
||||
OnChangeLanguage();
|
||||
disposable = GContext.OnEvent<ChangeLanguageEvent>().Subscribe(OnChangeLanguage);
|
||||
}
|
||||
|
||||
void OnChangeLanguage(ChangeLanguageEvent changeLanguageEvent = null)
|
||||
{
|
||||
string localized_text = LocalizationMgr.GetText(localizationId);
|
||||
|
||||
if (!string.IsNullOrEmpty(localized_text))
|
||||
{
|
||||
switch (textType)
|
||||
{
|
||||
case TextType.Text:
|
||||
if (text)
|
||||
{
|
||||
text.text = localized_text;
|
||||
}
|
||||
|
||||
return;
|
||||
case TextType.TextMeshPro_UGUI:
|
||||
if (text_UGUI)
|
||||
{
|
||||
text_UGUI.text = localized_text;
|
||||
}
|
||||
|
||||
return;
|
||||
case TextType.TextMeshPro:
|
||||
if (text_Pro)
|
||||
{
|
||||
text_Pro.text = localized_text;
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void OnDestroy()
|
||||
{
|
||||
if (disposable != null)
|
||||
{
|
||||
disposable.Dispose();
|
||||
disposable = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
11
Assets/Scripts/UIExtend/TextLocalization.cs.meta
Normal file
11
Assets/Scripts/UIExtend/TextLocalization.cs.meta
Normal file
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 67302bfe21c2841429242d7d1edc8f16
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
79
Assets/Scripts/UIExtend/UIButton.cs
Normal file
79
Assets/Scripts/UIExtend/UIButton.cs
Normal file
@@ -0,0 +1,79 @@
|
||||
using System;
|
||||
using UniRx;
|
||||
using UniRx.Triggers;
|
||||
using UnityEngine;
|
||||
using UnityEngine.EventSystems;
|
||||
using UnityEngine.UI;
|
||||
|
||||
public class UIButton : Button
|
||||
{
|
||||
[SerializeField]private float _holdThresh = 0.5f;
|
||||
private ButtonClickedEvent m_OnDown = new ButtonClickedEvent();
|
||||
private ButtonClickedEvent m_OnUp = new ButtonClickedEvent();
|
||||
private ButtonClickedEvent m_OnLongPress = new ButtonClickedEvent();
|
||||
private ButtonClickedEvent m_OnShortClick = new ButtonClickedEvent();
|
||||
private IDisposable clickStream, longPressStream;
|
||||
private DateTimeOffset _lastDownTime, _lastUpTime;
|
||||
//private CompositeDisposable _pressTimer;
|
||||
//private float ci;
|
||||
PointerEventData pointerEventData;
|
||||
protected override void Start()
|
||||
{
|
||||
var pointerDownStream = this.OnPointerDownAsObservable().Timestamp();
|
||||
var pointerUpStream = this.OnPointerUpAsObservable().Timestamp();
|
||||
//Logic of long press: delayed.Timestamp - delayThreshold - lastButtonUpTime > 0
|
||||
longPressStream = pointerDownStream
|
||||
.SelectMany(_ => Observable.Timer(TimeSpan.FromSeconds(_holdThresh)))
|
||||
.Timestamp()
|
||||
.Where(_ => (_.Timestamp - _lastUpTime ).TotalSeconds > _holdThresh)
|
||||
.Subscribe(_ => m_OnLongPress.Invoke())
|
||||
.AddTo(this);
|
||||
clickStream = pointerUpStream
|
||||
.CombineLatest(pointerDownStream, (eUp, eDown) => eUp.Timestamp - eDown.Timestamp)
|
||||
.Where(interval => interval.TotalSeconds > 0 && interval.TotalSeconds <= _holdThresh)
|
||||
.Subscribe(_ => m_OnShortClick.Invoke())
|
||||
.AddTo(this);
|
||||
}
|
||||
public ButtonClickedEvent onDown
|
||||
{
|
||||
get { return m_OnDown; }
|
||||
set { m_OnDown = value; }
|
||||
}
|
||||
|
||||
public ButtonClickedEvent onUp
|
||||
{
|
||||
get { return m_OnUp; }
|
||||
set { m_OnUp = value; }
|
||||
}
|
||||
|
||||
public ButtonClickedEvent onLongPress
|
||||
{
|
||||
get { return m_OnLongPress; }
|
||||
set { m_OnLongPress = value; }
|
||||
}
|
||||
|
||||
public new ButtonClickedEvent onClick
|
||||
{
|
||||
get { return m_OnShortClick; }
|
||||
set { m_OnShortClick = value; }
|
||||
}
|
||||
|
||||
public override void OnPointerDown(PointerEventData eventData)
|
||||
{
|
||||
base.OnPointerDown(eventData);
|
||||
m_OnDown.Invoke();
|
||||
pointerEventData = eventData;
|
||||
_lastDownTime = DateTimeOffset.Now;
|
||||
}
|
||||
public override void OnPointerUp(PointerEventData eventData)
|
||||
{
|
||||
base.OnPointerUp(eventData);
|
||||
m_OnUp.Invoke();
|
||||
_lastUpTime = DateTimeOffset.Now;
|
||||
}
|
||||
public void Up()
|
||||
{
|
||||
OnPointerUp(pointerEventData);
|
||||
}
|
||||
public bool IsDowning => IsPressed();
|
||||
}
|
||||
3
Assets/Scripts/UIExtend/UIButton.cs.meta
Normal file
3
Assets/Scripts/UIExtend/UIButton.cs.meta
Normal file
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 0bf32009b1c846b8b2750cdfb3419975
|
||||
timeCreated: 1694487538
|
||||
84
Assets/Scripts/UIExtend/UIButtonDouble.cs
Normal file
84
Assets/Scripts/UIExtend/UIButtonDouble.cs
Normal file
@@ -0,0 +1,84 @@
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
using UnityEngine.EventSystems;
|
||||
using UnityEngine.UI;
|
||||
using static UnityEngine.EventSystems.ExecuteEvents;
|
||||
|
||||
public class UIButtonDouble : Button
|
||||
{
|
||||
public int ExecuteCount = 1;
|
||||
public int curExecuteCount = 0;
|
||||
private ButtonClickedEvent m_OnDown = new ButtonClickedEvent();
|
||||
private ButtonClickedEvent m_OnUp = new ButtonClickedEvent();
|
||||
PointerEventData _pointerEventData;
|
||||
public PointerEventData PointerEventData => _pointerEventData;
|
||||
public ButtonClickedEvent onDown
|
||||
{
|
||||
get { return m_OnDown; }
|
||||
set { m_OnDown = value; }
|
||||
}
|
||||
|
||||
public ButtonClickedEvent onUp
|
||||
{
|
||||
get { return m_OnUp; }
|
||||
set { m_OnUp = value; }
|
||||
}
|
||||
public override void OnPointerClick(PointerEventData eventData)
|
||||
{
|
||||
RaycastThrough(eventData, pointerClickHandler);
|
||||
base.OnPointerClick(eventData);
|
||||
}
|
||||
|
||||
public override void OnPointerDown(PointerEventData eventData)
|
||||
{
|
||||
RaycastThrough(eventData, pointerDownHandler);
|
||||
m_OnDown.Invoke();
|
||||
base.OnPointerDown(eventData);
|
||||
}
|
||||
public override void OnPointerUp(PointerEventData eventData)
|
||||
{
|
||||
RaycastThrough(eventData, pointerUpHandler);
|
||||
m_OnUp.Invoke();
|
||||
base.OnPointerUp(eventData);
|
||||
}
|
||||
public void RaycastThrough<T>(BaseEventData baseEventData, EventFunction<T> eventFunction) where T : IEventSystemHandler
|
||||
{
|
||||
_pointerEventData = (PointerEventData)baseEventData;
|
||||
|
||||
List<RaycastResult> raycastResults = new List<RaycastResult>();
|
||||
//当前处理的gameobject
|
||||
GameObject currentObj = gameObject ?? _pointerEventData.pointerDrag;
|
||||
//获取当前射线检测到的所有结果
|
||||
EventSystem.current.RaycastAll(_pointerEventData, raycastResults);
|
||||
curExecuteCount = 0;
|
||||
bool isExecute = false;
|
||||
for (int i = 0; i < raycastResults.Count; i++)
|
||||
{
|
||||
//}
|
||||
//foreach (var item in raycastResults)
|
||||
//{
|
||||
var item = raycastResults[i];
|
||||
GameObject nextObj = item.gameObject;
|
||||
if (nextObj != null && nextObj == currentObj)
|
||||
{
|
||||
isExecute = true;
|
||||
}
|
||||
if (isExecute && nextObj != null && nextObj != currentObj)
|
||||
{
|
||||
GameObject excuteObj = GetEventHandler<T>(nextObj);
|
||||
if (excuteObj != null && excuteObj != currentObj)
|
||||
{
|
||||
//执行下一层事件,如果你还需要继续执行下面层UI事件可以不return,我这里只检测下一层的,所以我循环一次就直接return
|
||||
Execute(excuteObj, _pointerEventData, eventFunction);
|
||||
}
|
||||
curExecuteCount++;
|
||||
if (curExecuteCount >= ExecuteCount)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
11
Assets/Scripts/UIExtend/UIButtonDouble.cs.meta
Normal file
11
Assets/Scripts/UIExtend/UIButtonDouble.cs.meta
Normal file
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: e0b36a0614d5b804abb38c6bbfcb3ef9
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
80
Assets/Scripts/UIExtend/UIDrag.cs
Normal file
80
Assets/Scripts/UIExtend/UIDrag.cs
Normal file
@@ -0,0 +1,80 @@
|
||||
using UnityEngine;
|
||||
using UnityEngine.EventSystems;
|
||||
|
||||
public class UIDrag : MonoBehaviour, IBeginDragHandler, IDragHandler, IEndDragHandler, IPointerDownHandler, IPointerUpHandler
|
||||
{
|
||||
//按下回调
|
||||
public System.Action OnPointerDownCall;
|
||||
public System.Action OnPointerUpCall;
|
||||
//开始拖动回调
|
||||
public System.Action OnBeginDragCall;
|
||||
|
||||
//拖动中回调
|
||||
public System.Action<PointerEventData> OnDragCall;
|
||||
|
||||
//拖动结束回调
|
||||
public System.Action OnEndDragCall;
|
||||
bool isdrag = false;
|
||||
public bool isTouchTwo = false;
|
||||
|
||||
public void OnPointerDown(PointerEventData eventData)
|
||||
{
|
||||
OnPointerDownCall?.Invoke();
|
||||
}
|
||||
|
||||
public void OnPointerUp(PointerEventData eventData)
|
||||
{
|
||||
OnPointerUpCall?.Invoke();
|
||||
}
|
||||
|
||||
public void OnBeginDrag(PointerEventData eventData)
|
||||
{
|
||||
#if UNITY_EDITOR
|
||||
if (Input.GetMouseButton(0))
|
||||
#else
|
||||
if (Input.touchCount == 1 || Input.touchCount == 2)
|
||||
#endif
|
||||
{
|
||||
OnBeginDragCall?.Invoke();
|
||||
isdrag = true;
|
||||
}
|
||||
}
|
||||
|
||||
public void OnDrag(PointerEventData eventData)
|
||||
{
|
||||
OnDragCall?.Invoke(eventData);
|
||||
if (isTouchTwo)
|
||||
{
|
||||
#if !UNITY_EDITOR
|
||||
//if (Input.touchCount == 1)
|
||||
//{
|
||||
// if (!isdrag)
|
||||
// {
|
||||
// OnBeginDragCall?.Invoke();
|
||||
// isdrag = true;
|
||||
// }
|
||||
//}
|
||||
//else
|
||||
if (Input.touchCount == 2)
|
||||
{
|
||||
//双指操作
|
||||
if (isdrag)
|
||||
{
|
||||
OnEndDragCall?.Invoke();
|
||||
isdrag = false;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
public void OnEndDrag(PointerEventData eventData)
|
||||
{
|
||||
if (isdrag)
|
||||
{
|
||||
OnEndDragCall?.Invoke();
|
||||
isdrag = false;
|
||||
}
|
||||
}
|
||||
//双指操作
|
||||
}
|
||||
3
Assets/Scripts/UIExtend/UIDrag.cs.meta
Normal file
3
Assets/Scripts/UIExtend/UIDrag.cs.meta
Normal file
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 909da6861b624f80826aa23bef9ad875
|
||||
timeCreated: 1694420922
|
||||
Reference in New Issue
Block a user