diff --git a/Assets/AddressableAssetsData/AssetGroups/Localization-Assets-Shared.asset b/Assets/AddressableAssetsData/AssetGroups/Localization-Assets-Shared.asset index cf3d61f15..8e4c50156 100644 --- a/Assets/AddressableAssetsData/AssetGroups/Localization-Assets-Shared.asset +++ b/Assets/AddressableAssetsData/AssetGroups/Localization-Assets-Shared.asset @@ -171,6 +171,13 @@ MonoBehaviour: m_ReadOnly: 1 m_SerializedLabels: [] FlaggedDuringContentUpdateRestriction: 0 + - m_GUID: 4b7004709baa3f942998e7994b48bd38 + m_Address: "\u7B7E\u540D\u5B57\u8FF9" + m_ReadOnly: 1 + m_SerializedLabels: + - Locale-en + - Locale-zh-Hans + FlaggedDuringContentUpdateRestriction: 0 m_ReadOnly: 1 m_Settings: {fileID: 11400000, guid: 77169ce22e430f64fb36c771815a4a7b, type: 2} m_SchemaSet: diff --git a/Assets/Language/Sprite/sprite Shared Data.asset b/Assets/Language/Sprite/sprite Shared Data.asset index f4be944b0..bea1b4208 100644 --- a/Assets/Language/Sprite/sprite Shared Data.asset +++ b/Assets/Language/Sprite/sprite Shared Data.asset @@ -19,6 +19,10 @@ MonoBehaviour: m_Key: community m_Metadata: m_Items: [] + - m_Id: 28253191807176704 + m_Key: signboard + m_Metadata: + m_Items: [] m_Metadata: m_Items: - rid: 8879661439621529603 @@ -39,5 +43,9 @@ MonoBehaviour: m_TableCodes: - en - zh-Hans + - m_KeyId: 28253191807176704 + m_TableCodes: + - en + - zh-Hans m_TypeString: UnityEngine.Sprite, UnityEngine.CoreModule, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null diff --git a/Assets/Language/Sprite/sprite_en.asset b/Assets/Language/Sprite/sprite_en.asset index ab5100a81..12fc5c456 100644 --- a/Assets/Language/Sprite/sprite_en.asset +++ b/Assets/Language/Sprite/sprite_en.asset @@ -22,6 +22,10 @@ MonoBehaviour: m_Localized: 032b1b5e09139ae45b6de51d35baae79[discord-square-color-icon] m_Metadata: m_Items: [] + - m_Id: 28253191807176704 + m_Localized: 4b7004709baa3f942998e7994b48bd38[SignHere] + m_Metadata: + m_Items: [] references: version: 2 RefIds: [] diff --git a/Assets/Language/Sprite/sprite_zh-Hans.asset b/Assets/Language/Sprite/sprite_zh-Hans.asset index 3fe0429ff..0410df24c 100644 --- a/Assets/Language/Sprite/sprite_zh-Hans.asset +++ b/Assets/Language/Sprite/sprite_zh-Hans.asset @@ -22,6 +22,10 @@ MonoBehaviour: m_Localized: 6d8ee72f781acf64abfe82ab659f5771[03_qq_symbol] m_Metadata: m_Items: [] + - m_Id: 28253191807176704 + m_Localized: "4b7004709baa3f942998e7994b48bd38[\u6B64\u5904\u7B7E\u540D]" + m_Metadata: + m_Items: [] references: version: 2 RefIds: [] diff --git a/Assets/Scenes/Persistence.unity b/Assets/Scenes/Persistence.unity index 813f91400..2cd0f267c 100644 --- a/Assets/Scenes/Persistence.unity +++ b/Assets/Scenes/Persistence.unity @@ -16430,6 +16430,7 @@ GameObject: - component: {fileID: 1141106672} - component: {fileID: 1141106674} - component: {fileID: 1141106673} + - component: {fileID: 1141106675} m_Layer: 5 m_Name: Sign Borad m_TagString: Untagged @@ -16452,7 +16453,7 @@ RectTransform: - {fileID: 1986485322} - {fileID: 1689982975} - {fileID: 1890646708} - - {fileID: 1150236047} + - {fileID: 1609613730} m_Father: {fileID: 300534481} m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} m_AnchorMin: {x: 0, y: 0} @@ -16498,81 +16499,20 @@ CanvasRenderer: m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 1141106671} m_CullTransparentMesh: 1 ---- !u!1 &1150236046 -GameObject: - m_ObjectHideFlags: 0 - m_CorrespondingSourceObject: {fileID: 0} - m_PrefabInstance: {fileID: 0} - m_PrefabAsset: {fileID: 0} - serializedVersion: 6 - m_Component: - - component: {fileID: 1150236047} - - component: {fileID: 1150236049} - - component: {fileID: 1150236048} - m_Layer: 5 - m_Name: "\u6807\u5FD7" - m_TagString: Untagged - m_Icon: {fileID: 0} - m_NavMeshLayer: 0 - m_StaticEditorFlags: 0 - m_IsActive: 1 ---- !u!224 &1150236047 -RectTransform: - m_ObjectHideFlags: 0 - m_CorrespondingSourceObject: {fileID: 0} - m_PrefabInstance: {fileID: 0} - m_PrefabAsset: {fileID: 0} - m_GameObject: {fileID: 1150236046} - m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} - m_LocalPosition: {x: 0, y: 0, z: 0} - m_LocalScale: {x: 1, y: 1, z: 1} - m_ConstrainProportionsScale: 0 - m_Children: [] - m_Father: {fileID: 1141106672} - m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} - m_AnchorMin: {x: 0.5, y: 0.5} - m_AnchorMax: {x: 0.5, y: 0.5} - m_AnchoredPosition: {x: 360.3, y: -264} - m_SizeDelta: {x: 32, y: 32} - m_Pivot: {x: 0.5, y: 0.5} ---- !u!114 &1150236048 +--- !u!114 &1141106675 MonoBehaviour: m_ObjectHideFlags: 0 m_CorrespondingSourceObject: {fileID: 0} m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} - m_GameObject: {fileID: 1150236046} + m_GameObject: {fileID: 1141106671} m_Enabled: 1 m_EditorHideFlags: 0 - m_Script: {fileID: 11500000, guid: fe87c0e1cc204ed48ad3b37840f39efc, type: 3} + m_Script: {fileID: 11500000, guid: d94a8c4a249bd4c40acd08b5b22b816f, type: 3} m_Name: m_EditorClassIdentifier: - m_Material: {fileID: 0} - m_Color: {r: 0.5647059, g: 0.5764706, b: 0.6, a: 1} - m_RaycastTarget: 1 - m_RaycastPadding: {x: 0, y: 0, z: 0, w: 0} - m_Maskable: 1 - m_OnCullStateChanged: - m_PersistentCalls: - m_Calls: [] - m_Sprite: {fileID: 21300000, guid: 9292740f0fa1fcf42b3bb16ce66d2355, type: 3} - m_Type: 0 - m_PreserveAspect: 0 - m_FillCenter: 1 - m_FillMethod: 4 - m_FillAmount: 1 - m_FillClockwise: 1 - m_FillOrigin: 0 - m_UseSpriteMesh: 0 - m_PixelsPerUnitMultiplier: 1 ---- !u!222 &1150236049 -CanvasRenderer: - m_ObjectHideFlags: 0 - m_CorrespondingSourceObject: {fileID: 0} - m_PrefabInstance: {fileID: 0} - m_PrefabAsset: {fileID: 0} - m_GameObject: {fileID: 1150236046} - m_CullTransparentMesh: 1 + signArea: {fileID: 1890646709} + icon: {fileID: 1609613731} --- !u!1 &1153212192 GameObject: m_ObjectHideFlags: 0 @@ -22851,6 +22791,81 @@ MonoBehaviour: m_EditorClassIdentifier: m_HorizontalFit: 2 m_VerticalFit: 0 +--- !u!1 &1609613729 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 1609613730} + - component: {fileID: 1609613732} + - component: {fileID: 1609613731} + m_Layer: 5 + m_Name: "\u6807\u5FD7" + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 0 +--- !u!224 &1609613730 +RectTransform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1609613729} + m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} + m_LocalPosition: {x: 0, y: 0, z: 0} + m_LocalScale: {x: 1, y: 1, z: 1} + m_ConstrainProportionsScale: 0 + m_Children: [] + m_Father: {fileID: 1141106672} + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} + m_AnchorMin: {x: 0.5, y: 0.5} + m_AnchorMax: {x: 0.5, y: 0.5} + m_AnchoredPosition: {x: 352, y: -264} + m_SizeDelta: {x: 32, y: 32} + m_Pivot: {x: 0.5, y: 0.5} +--- !u!114 &1609613731 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1609613729} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: fe87c0e1cc204ed48ad3b37840f39efc, type: 3} + m_Name: + m_EditorClassIdentifier: + m_Material: {fileID: 0} + m_Color: {r: 0.9622642, g: 0.15129934, b: 0.15129934, a: 1} + m_RaycastTarget: 1 + m_RaycastPadding: {x: 0, y: 0, z: 0, w: 0} + m_Maskable: 1 + m_OnCullStateChanged: + m_PersistentCalls: + m_Calls: [] + m_Sprite: {fileID: 21300000, guid: 566787ba43e2a5646bdabb62bd6845f2, type: 3} + m_Type: 0 + m_PreserveAspect: 0 + m_FillCenter: 1 + m_FillMethod: 4 + m_FillAmount: 1 + m_FillClockwise: 1 + m_FillOrigin: 0 + m_UseSpriteMesh: 0 + m_PixelsPerUnitMultiplier: 1 +--- !u!222 &1609613732 +CanvasRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1609613729} + m_CullTransparentMesh: 1 --- !u!1 &1611684199 GameObject: m_ObjectHideFlags: 0 @@ -23737,8 +23752,9 @@ GameObject: - component: {fileID: 1689982975} - component: {fileID: 1689982978} - component: {fileID: 1689982977} + - component: {fileID: 1689982979} m_Layer: 5 - m_Name: "\u7B7E\u540D\u533A\u52A8\u6548" + m_Name: "\u7B7E\u540D\u793A\u4F8B" m_TagString: Untagged m_Icon: {fileID: 0} m_NavMeshLayer: 0 @@ -23761,7 +23777,7 @@ RectTransform: m_AnchorMin: {x: 0.5, y: 0.5} m_AnchorMax: {x: 0.5, y: 0.5} m_AnchoredPosition: {x: 185, y: -307} - m_SizeDelta: {x: 302, y: 73} + m_SizeDelta: {x: 353, y: 100} m_Pivot: {x: 0.5, y: 0.5} --- !u!114 &1689982977 MonoBehaviour: @@ -23776,14 +23792,14 @@ MonoBehaviour: m_Name: m_EditorClassIdentifier: m_Material: {fileID: 0} - m_Color: {r: 1, g: 1, b: 1, a: 1} + m_Color: {r: 1, g: 1, b: 1, a: 0.78431374} m_RaycastTarget: 1 m_RaycastPadding: {x: 0, y: 0, z: 0, w: 0} m_Maskable: 1 m_OnCullStateChanged: m_PersistentCalls: m_Calls: [] - m_Sprite: {fileID: 0} + m_Sprite: {fileID: 21300000, guid: bf7044addc71591418e2ac882eff1939, type: 3} m_Type: 0 m_PreserveAspect: 0 m_FillCenter: 1 @@ -23801,6 +23817,47 @@ CanvasRenderer: m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 1689982974} m_CullTransparentMesh: 1 +--- !u!114 &1689982979 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1689982974} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 69beb381e244f92449b8c4cf954630e9, type: 3} + m_Name: + m_EditorClassIdentifier: + m_TrackedObjects: + - rid: 1784249405823778818 + references: + version: 2 + RefIds: + - rid: 1784249405823778818 + type: {class: TrackedUGuiGraphic, ns: UnityEngine.Localization.PropertyVariants.TrackedObjects, asm: Unity.Localization} + data: + m_Target: {fileID: 1689982977} + m_TrackedProperties: + items: + - rid: 1784249405823778819 + m_UpdateType: 0 + - rid: 1784249405823778819 + type: {class: LocalizedAssetProperty, ns: UnityEngine.Localization.PropertyVariants.TrackedProperties, asm: Unity.Localization} + data: + m_Localized: + rid: 1784249405823778820 + m_PropertyPath: m_Sprite + - rid: 1784249405823778820 + type: {class: LocalizedSprite, ns: UnityEngine.Localization, asm: Unity.Localization} + data: + m_TableReference: + m_TableCollectionName: GUID:9abdd56d318b1ec459c7e866748d6878 + m_TableEntryReference: + m_KeyId: 28253191807176704 + m_Key: + m_FallbackState: 0 + m_WaitForCompletion: 0 --- !u!1 &1694277408 GameObject: m_ObjectHideFlags: 0 @@ -25505,7 +25562,7 @@ GameObject: m_Icon: {fileID: 0} m_NavMeshLayer: 0 m_StaticEditorFlags: 0 - m_IsActive: 0 + m_IsActive: 1 --- !u!224 &1890646708 RectTransform: m_ObjectHideFlags: 0 @@ -25542,9 +25599,10 @@ MonoBehaviour: backgroundColor: serializedVersion: 2 rgba: 16777215 - drawColor: {r: 0, g: 0, b: 0, a: 1} - brushSize: 5 + drawColor: {r: 1, g: 0, b: 0.14117648, a: 1} + brushSize: 4 smoothDrawing: 1 + targetLength: 550 --- !u!114 &1890646710 MonoBehaviour: m_ObjectHideFlags: 0 diff --git a/Assets/Scripts/Dialog System/BaseYarnCommand.cs b/Assets/Scripts/Dialog System/BaseYarnCommand.cs index 1bf5e48b2..71d3e9f43 100644 --- a/Assets/Scripts/Dialog System/BaseYarnCommand.cs +++ b/Assets/Scripts/Dialog System/BaseYarnCommand.cs @@ -135,6 +135,31 @@ namespace AibisDream return panel.FlashRed(duration); } + [YarnCommand("show_sign_board")] + public static void ShowSignBoard(bool enable = true) + { + var panel = UIManager.Instance.GetPanel(); + panel.ShowSignBoard(); + if (enable) + { + panel.EnableSignBoard(); + } + } + + [YarnCommand("enable_sign_board")] + public static void EnableSignBoard() + { + var panel = UIManager.Instance.GetPanel(); + panel.EnableSignBoard(); + } + + [YarnCommand("close_sign_board")] + public static void CloseSignBoard() + { + var panel = UIManager.Instance.GetPanel(); + panel.CloseSignBoard(); + } + #endregion } } \ No newline at end of file diff --git a/Assets/Scripts/Framework/Core/UnityTool.cs b/Assets/Scripts/Framework/Core/UnityTool.cs index 68b87553b..f4512710c 100644 --- a/Assets/Scripts/Framework/Core/UnityTool.cs +++ b/Assets/Scripts/Framework/Core/UnityTool.cs @@ -1,10 +1,13 @@ using System; +using System.Collections; using System.Collections.Generic; using System.Linq; using System.Reflection; +using DG.Tweening; using UnityEngine; +using UnityEngine.UI; -namespace Framework.Core +namespace AibisDream.Framework { [AttributeUsage(AttributeTargets.Field, AllowMultiple = false)] public class FindComponentAttribute : Attribute diff --git a/Assets/Scripts/Framework/OtherKit/CurtainRevealKit.cs b/Assets/Scripts/Framework/OtherKit/CurtainRevealKit.cs new file mode 100644 index 000000000..49f545d1e --- /dev/null +++ b/Assets/Scripts/Framework/OtherKit/CurtainRevealKit.cs @@ -0,0 +1,154 @@ +using System.Collections; +using DG.Tweening; +using UnityEngine; +using UnityEngine.UI; + +namespace AibisDream.Framework +{ + /// + /// UI Image窗帘展开效果工具类(使用Shader实现) + /// 实现从左到右的展开动画,保持图片原始宽高比不被压扁 + /// + public static class CurtainRevealKit + { + private static readonly int RevealProgressPropertyID = Shader.PropertyToID("_RevealProgress"); + private static readonly int EdgeSoftnessPropertyID = Shader.PropertyToID("_EdgeSoftness"); + + /// + /// 从左到右显示图片(窗帘拉开效果),使用Shader实现,保持图片原始宽高比 + /// + /// 目标Image组件 + /// 动画时长 + /// 缓动类型 + /// 边缘柔化程度(0-0.1) + /// 协程 + public static IEnumerator RevealFromLeftAsync(this Image image, float duration = 1f, + Ease easeType = Ease.OutQuad, float edgeSoftness = 0.02f) + { + image.gameObject.SetActive(true); + + // 确保Image有Material实例(避免影响其他使用相同Material的对象) + Material material = EnsureMaterialInstance(image); + + // 设置边缘柔化 + material.SetFloat(EdgeSoftnessPropertyID, edgeSoftness); + + // 初始状态:进度为0(完全隐藏) + material.SetFloat(RevealProgressPropertyID, 0f); + + // 动画:进度从0到1 + float currentProgress = 0f; + Tweener tween = DOTween.To( + () => currentProgress, + value => + { + currentProgress = value; + material.SetFloat(RevealProgressPropertyID, value); + }, + 1f, + duration + ).SetEase(easeType); + + yield return tween.WaitForCompletion(); + } + + /// + /// 同步版本:从左到右显示图片 + /// + /// 目标Image组件 + /// 动画时长 + /// 缓动类型 + /// 边缘柔化程度(0-0.1) + public static void RevealFromLeft(this Image image, float duration = 1f, + Ease easeType = Ease.OutQuad, float edgeSoftness = 0.02f) + { + image.gameObject.SetActive(true); + + Material material = EnsureMaterialInstance(image); + material.SetFloat(EdgeSoftnessPropertyID, edgeSoftness); + material.SetFloat(RevealProgressPropertyID, 0f); + + float currentProgress = 0f; + DOTween.To( + () => currentProgress, + value => + { + currentProgress = value; + material.SetFloat(RevealProgressPropertyID, value); + }, + 1f, + duration + ).SetEase(easeType); + } + + /// + /// 设置窗帘展开进度(手动控制) + /// + /// 目标Image组件 + /// 进度值(0-1) + /// 边缘柔化程度(0-0.1) + public static void SetRevealProgress(this Image image, float progress, float edgeSoftness = 0.02f) + { + if (image == null || image.material == null) return; + + Material material = EnsureMaterialInstance(image); + material.SetFloat(EdgeSoftnessPropertyID, edgeSoftness); + material.SetFloat(RevealProgressPropertyID, Mathf.Clamp01(progress)); + } + + /// + /// 重置窗帘展开效果(恢复原始状态) + /// + /// 目标Image组件 + public static void ResetReveal(this Image image) + { + if (image == null || image.material == null) return; + + Material material = EnsureMaterialInstance(image); + material.SetFloat(RevealProgressPropertyID, 1f); + } + + /// + /// 确保Material有独立实例(避免影响其他对象) + /// + private static Material EnsureMaterialInstance(Image image) + { + if (image.material == null) + { + // 如果没有Material,尝试加载Shader + Shader shader = Shader.Find("UI/CurtainReveal"); + if (shader == null) + { + Debug.LogError("找不到Shader: UI/CurtainReveal,请确保Shader已正确导入"); + return null; + } + image.material = new Material(shader); + } + else if (image.material.shader.name != "UI/CurtainReveal") + { + // 如果Material的Shader不对,创建新的Material + Shader shader = Shader.Find("UI/CurtainReveal"); + if (shader == null) + { + Debug.LogError("找不到Shader: UI/CurtainReveal,请确保Shader已正确导入"); + return image.material; + } + image.material = new Material(shader); + // 复制原始纹理和颜色 + if (image.sprite != null) + { + image.material.SetTexture("_MainTex", image.sprite.texture); + } + image.material.SetColor("_Color", image.color); + } + else if (image.material.name.Contains("(Instance)") == false) + { + // 如果Material不是实例,创建实例以避免影响其他对象 + image.material = new Material(image.material); + } + + return image.material; + } + } +} + diff --git a/Assets/Scripts/Framework/OtherKit/CurtainRevealKit.cs.meta b/Assets/Scripts/Framework/OtherKit/CurtainRevealKit.cs.meta new file mode 100644 index 000000000..7c5c8e080 --- /dev/null +++ b/Assets/Scripts/Framework/OtherKit/CurtainRevealKit.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 5fdfd099f63f488468ea20b150b8e883 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Scripts/UI/Panel/PlayToolPanel.cs b/Assets/Scripts/UI/Panel/PlayToolPanel.cs index d388ace57..468c7b724 100644 --- a/Assets/Scripts/UI/Panel/PlayToolPanel.cs +++ b/Assets/Scripts/UI/Panel/PlayToolPanel.cs @@ -28,6 +28,7 @@ namespace AibisDream.UI private Image _redFlash; private ModalWindowProgressBar _window; private ModalWindow _warningWindow; + private SignBoard _signBoard; #endregion @@ -46,6 +47,7 @@ namespace AibisDream.UI _window = transform.Find("进度条窗口").GetComponent(); _redFlash = transform.Find("Red Flash").GetComponent(); _warningWindow = transform.Find("警告窗口").GetComponent(); + _signBoard = transform.GetComponentInChildren(); } public IEnumerator FadeInAsync(float duration = 1) @@ -160,6 +162,21 @@ namespace AibisDream.UI _redFlash.gameObject.SetActive(false); } + public void ShowSignBoard() + { + _signBoard.ShowSignBoard(); + } + + public void EnableSignBoard() + { + _signBoard.EnableSignBoard(); + } + + public void CloseSignBoard() + { + _signBoard.CloseSignBoard(); + } + private void HideAll() { _fullScreenImage.FadeOut(0.1f); diff --git a/Assets/Scripts/UI/Panel/SignBoard/SignArea.cs b/Assets/Scripts/UI/Panel/SignBoard/SignArea.cs index 7fca9ac64..86d387815 100644 --- a/Assets/Scripts/UI/Panel/SignBoard/SignArea.cs +++ b/Assets/Scripts/UI/Panel/SignBoard/SignArea.cs @@ -1,9 +1,11 @@ +using System; using UnityEngine; +using UnityEngine.Events; using UnityEngine.EventSystems; using UnityEngine.UI; namespace AibisDream -{ +{ /// /// 画板组件,支持鼠标拖动绘制(UI Space) /// 需要挂载在带有RectTransform和RawImage的GameObject上 @@ -22,44 +24,68 @@ namespace AibisDream [SerializeField] private int brushSize = 5; [SerializeField] private bool smoothDrawing = true; // 是否平滑绘制(连接两点之间的所有点) + [Header("长度目标设置")] + [SerializeField] private float targetLength = 100f; // 目标绘画长度(像素) + + public event Action onLengthTargetExceeded; // 绘画长度超过目标事件(参数:当前长度) + private Texture2D drawTexture; private RawImage drawImage; private RectTransform rectTransform; private Vector2 lastDrawPoint; private bool isDrawing = false; - + private float currentStrokeLength = 0f; // 当前单次绘画的线段长度 + private float totalLength = 0f; // 累计所有笔画的总长度 + private bool hasTriggeredTargetEvent = false; // 是否已触发超过目标事件 + private bool isLocked = true; // 锁定标记,锁定状态下无法绘画 + private void Awake() { drawImage = GetComponent(); rectTransform = GetComponent(); } - - private void Start() - { - InitializeTexture(); - } - + /// /// 初始化画布纹理 /// - private void InitializeTexture() + public void Init() { // 创建可读写的纹理 drawTexture = new Texture2D(textureWidth, textureHeight, TextureFormat.RGBA32, false); - drawTexture.filterMode = FilterMode.Bilinear; - + drawTexture.filterMode = FilterMode.Point; // 使用 Point 过滤模式实现像素化效果 + // 填充背景色 - Color[] pixels = new Color[textureWidth * textureHeight]; + Color32[] pixels = new Color32[textureWidth * textureHeight]; for (int i = 0; i < pixels.Length; i++) { pixels[i] = backgroundColor; } - drawTexture.SetPixels(pixels); + drawTexture.SetPixels32(pixels); drawTexture.Apply(); - + // 设置到RawImage drawImage.texture = drawTexture; + + // 重置累计总长度和事件标志 + totalLength = 0f; + hasTriggeredTargetEvent = false; + } + + public void Dispose() + { + Clear(); + + if (drawTexture != null) + { + Destroy(drawTexture); + } + drawTexture = null; + lastDrawPoint = Vector2.zero; + isDrawing = false; + onLengthTargetExceeded = null; + // Dispose 后锁定 + isLocked = true; } /// @@ -94,38 +120,41 @@ namespace AibisDream } /// - /// 在指定位置绘制 + /// 在指定位置绘制(方形像素笔刷) /// private void DrawAtPoint(Vector2 point) { + // 检查纹理是否已初始化 + if (drawTexture == null) + { + Debug.LogWarning("SignArea: drawTexture 未初始化,请先调用 Init() 方法"); + return; + } + int centerX = Mathf.RoundToInt(point.x); int centerY = Mathf.RoundToInt(point.y); - // 绘制圆形笔刷 + // 绘制方形笔刷(像素化,无柔边效果) for (int x = -brushSize; x <= brushSize; x++) { for (int y = -brushSize; y <= brushSize; y++) { - // 检查是否在圆形范围内 - float distance = Mathf.Sqrt(x * x + y * y); - if (distance <= brushSize) + int px = centerX + x; + int py = centerY + y; + + // 检查边界 + if (px >= 0 && px < textureWidth && py >= 0 && py < textureHeight) { - int px = centerX + x; - int py = centerY + y; - - // 检查边界 - if (px >= 0 && px < textureWidth && py >= 0 && py < textureHeight) - { - // 根据距离计算透明度,实现柔和的笔刷效果 - float alpha = 1f - (distance / brushSize); - Color finalColor = drawColor; - finalColor.a *= alpha; - - // 混合颜色 - Color existingColor = drawTexture.GetPixel(px, py); - Color blendedColor = Color.Lerp(existingColor, finalColor, finalColor.a); - drawTexture.SetPixel(px, py, blendedColor); - } + // 像素化绘制:直接使用画笔颜色,不进行透明度渐变 + Color existingColor = drawTexture.GetPixel(px, py); + // Alpha 混合公式: result = src * srcAlpha + dst * (1 - srcAlpha) + Color blendedColor = new Color( + Mathf.Lerp(existingColor.r, drawColor.r, drawColor.a), + Mathf.Lerp(existingColor.g, drawColor.g, drawColor.a), + Mathf.Lerp(existingColor.b, drawColor.b, drawColor.a), + Mathf.Min(1f, existingColor.a + drawColor.a * (1f - existingColor.a)) + ); + drawTexture.SetPixel(px, py, blendedColor); } } } @@ -168,13 +197,24 @@ namespace AibisDream /// public void Clear() { - Color[] pixels = new Color[textureWidth * textureHeight]; + if (drawTexture == null) + { + Debug.LogWarning("SignArea: drawTexture 未初始化,无法清除画布"); + return; + } + + Color32[] pixels = new Color32[textureWidth * textureHeight]; for (int i = 0; i < pixels.Length; i++) { pixels[i] = backgroundColor; } - drawTexture.SetPixels(pixels); + drawTexture.SetPixels32(pixels); drawTexture.Apply(); + + // 重置累计总长度和事件标志 + totalLength = 0f; + hasTriggeredTargetEvent = false; + onLengthTargetExceeded = null; } /// @@ -201,11 +241,54 @@ namespace AibisDream backgroundColor = color; } + /// + /// 设置目标绘画长度 + /// + public void SetTargetLength(float length) + { + targetLength = Mathf.Max(0f, length); + } + + /// + /// 获取累计所有笔画的总长度 + /// + public float GetCurrentStrokeLength() + { + return totalLength; + } + + /// + /// 设置锁定状态 + /// + /// true 为锁定(无法绘画),false 为解锁(可以绘画) + public void SetLocked(bool locked) + { + isLocked = locked; + // 如果锁定,停止当前绘画 + if (locked && isDrawing) + { + isDrawing = false; + } + } + + /// + /// 获取当前锁定状态 + /// + public bool IsLocked() + { + return isLocked; + } + /// /// 保存画布为PNG图片 /// public byte[] SaveToPNG() { + if (drawTexture == null) + { + Debug.LogWarning("SignArea: drawTexture 未初始化,无法保存图片"); + return null; + } return drawTexture.EncodeToPNG(); } @@ -215,9 +298,24 @@ namespace AibisDream if (eventData.button != PointerEventData.InputButton.Left) return; + // 检查是否锁定 + if (isLocked) + { + return; + } + + // 检查纹理是否已初始化 + if (drawTexture == null) + { + Debug.LogWarning("SignArea: drawTexture 未初始化,请先调用 Init() 方法"); + return; + } + if (ScreenToTextureCoord(eventData.position, out Vector2 textureCoord)) { isDrawing = true; + // 重置当前绘画会话的累计长度 + currentStrokeLength = 0f; // 确保第一次点击也能绘制 lastDrawPoint = textureCoord; DrawAtPoint(textureCoord); @@ -236,26 +334,38 @@ namespace AibisDream if (!isDrawing) return; + // 检查是否锁定 + if (isLocked) + { + isDrawing = false; + return; + } + if (ScreenToTextureCoord(eventData.position, out Vector2 textureCoord)) { + float segmentLength = Vector2.Distance(lastDrawPoint, textureCoord); + if (smoothDrawing) { // 平滑绘制:连接两点之间的所有点 // 如果两点距离太近,直接绘制当前点(避免DrawLine计算出错) - float distance = Vector2.Distance(lastDrawPoint, textureCoord); - if (distance < 0.1f) + if (segmentLength < 0.1f) { DrawAtPoint(textureCoord); } else { DrawLine(lastDrawPoint, textureCoord); + // 累计线段长度 + currentStrokeLength += segmentLength; } } else { - // 直接绘制当前点 + // 直接绘制当前点,但仍累计线段长度 DrawAtPoint(textureCoord); + // 累计线段长度 + currentStrokeLength += segmentLength; } ApplyDrawing(); @@ -265,6 +375,21 @@ namespace AibisDream public void OnPointerUp(PointerEventData eventData) { + if (isDrawing) + { + // 将当前笔画长度累加到总长度 + totalLength += currentStrokeLength; + + // 检查累计总长度是否超过目标长度,且尚未触发过事件 + if (!hasTriggeredTargetEvent && totalLength > targetLength) + { + hasTriggeredTargetEvent = true; + // 触发超过目标长度事件,传递累计总长度 + onLengthTargetExceeded?.Invoke(totalLength); + // 触发事件后自动锁定 + isLocked = true; + } + } isDrawing = false; } diff --git a/Assets/Scripts/UI/Panel/SignBoard/SignBoard.cs b/Assets/Scripts/UI/Panel/SignBoard/SignBoard.cs index e9f37e3f3..904c4c90c 100644 --- a/Assets/Scripts/UI/Panel/SignBoard/SignBoard.cs +++ b/Assets/Scripts/UI/Panel/SignBoard/SignBoard.cs @@ -1,12 +1,46 @@ -using System.Collections; -using System.Collections.Generic; +using AibisDream.Framework; +using DG.Tweening; using UnityEngine; +using UnityEngine.UI; namespace AibisDream.UI { public class SignBoard : MonoBehaviour { [SerializeField] private SignArea signArea; - + [SerializeField] private Image icon; + + public void ShowSignBoard() + { + gameObject.SetActive(true); + + signArea.Init(); + signArea.onLengthTargetExceeded += OnSignEnd; + + icon.gameObject.SetActive(false); + icon.ResetReveal(); + } + + public void EnableSignBoard() + { + signArea.SetLocked(false); + } + + public void CloseSignBoard() + { + gameObject.SetActive(false); + + signArea.onLengthTargetExceeded -= OnSignEnd; + signArea.Dispose(); // Dispose 也会锁定,但这里显式调用确保状态正确 + + icon.gameObject.SetActive(false); + icon.ResetReveal(); + } + + private void OnSignEnd(float distance) + { + // 签名完成,打开成功标志 + StartCoroutine(icon.RevealFromLeftAsync(1f, Ease.OutQuad)); + } } } diff --git a/Assets/UI/Art/Sign Here.png b/Assets/UI/Art/Sign Here.png deleted file mode 100644 index 8df2418f8..000000000 --- a/Assets/UI/Art/Sign Here.png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:75e4d519854c26f046bd70da7a865703eed54c94deeebb1825173a650161e649 -size 4691 diff --git a/Assets/UI/Art/此处签名.png b/Assets/UI/Art/此处签名.png deleted file mode 100644 index adda11524..000000000 --- a/Assets/UI/Art/此处签名.png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:01f2c17d5dfd9f66527b8f775640b297553d1da83daefc80e1405f6921bc59e4 -size 4739 diff --git a/Assets/UI/Art/此处签名.png.meta b/Assets/UI/Art/此处签名.png.meta deleted file mode 100644 index 7729dced2..000000000 --- a/Assets/UI/Art/此处签名.png.meta +++ /dev/null @@ -1,153 +0,0 @@ -fileFormatVersion: 2 -guid: bf7044addc71591418e2ac882eff1939 -TextureImporter: - internalIDToNameTable: [] - externalObjects: {} - serializedVersion: 12 - mipmaps: - mipMapMode: 0 - enableMipMap: 0 - sRGBTexture: 1 - linearTexture: 0 - fadeOut: 0 - borderMipMap: 0 - mipMapsPreserveCoverage: 0 - alphaTestReferenceValue: 0.5 - mipMapFadeDistanceStart: 1 - mipMapFadeDistanceEnd: 3 - bumpmap: - convertToNormalMap: 0 - externalNormalMap: 0 - heightScale: 0.25 - normalMapFilter: 0 - flipGreenChannel: 0 - isReadable: 0 - streamingMipmaps: 0 - streamingMipmapsPriority: 0 - vTOnly: 0 - ignoreMipmapLimit: 0 - grayScaleToAlpha: 0 - generateCubemap: 6 - cubemapConvolution: 0 - seamlessCubemap: 0 - textureFormat: 1 - maxTextureSize: 2048 - textureSettings: - serializedVersion: 2 - filterMode: 1 - aniso: 1 - mipBias: 0 - wrapU: 1 - wrapV: 1 - wrapW: 1 - nPOTScale: 0 - lightmap: 0 - compressionQuality: 50 - spriteMode: 1 - spriteExtrude: 1 - spriteMeshType: 1 - alignment: 0 - spritePivot: {x: 0.5, y: 0.5} - spritePixelsToUnits: 100 - spriteBorder: {x: 0, y: 0, z: 0, w: 0} - spriteGenerateFallbackPhysicsShape: 1 - alphaUsage: 1 - alphaIsTransparency: 1 - spriteTessellationDetail: -1 - textureType: 8 - textureShape: 1 - singleChannelComponent: 0 - flipbookRows: 1 - flipbookColumns: 1 - maxTextureSizeSet: 0 - compressionQualitySet: 0 - textureFormatSet: 0 - ignorePngGamma: 0 - applyGammaDecoding: 0 - swizzle: 50462976 - cookieLightType: 0 - platformSettings: - - serializedVersion: 3 - buildTarget: DefaultTexturePlatform - maxTextureSize: 2048 - resizeAlgorithm: 0 - textureFormat: -1 - textureCompression: 1 - compressionQuality: 50 - crunchedCompression: 0 - allowsAlphaSplitting: 0 - overridden: 0 - ignorePlatformSupport: 0 - androidETC2FallbackOverride: 0 - forceMaximumCompressionQuality_BC6H_BC7: 0 - - serializedVersion: 3 - buildTarget: WebGL - maxTextureSize: 2048 - resizeAlgorithm: 0 - textureFormat: -1 - textureCompression: 1 - compressionQuality: 50 - crunchedCompression: 0 - allowsAlphaSplitting: 0 - overridden: 0 - ignorePlatformSupport: 0 - androidETC2FallbackOverride: 0 - forceMaximumCompressionQuality_BC6H_BC7: 0 - - serializedVersion: 3 - buildTarget: Standalone - maxTextureSize: 2048 - resizeAlgorithm: 0 - textureFormat: -1 - textureCompression: 1 - compressionQuality: 50 - crunchedCompression: 0 - allowsAlphaSplitting: 0 - overridden: 0 - ignorePlatformSupport: 0 - androidETC2FallbackOverride: 0 - forceMaximumCompressionQuality_BC6H_BC7: 0 - - serializedVersion: 3 - buildTarget: Android - maxTextureSize: 2048 - resizeAlgorithm: 0 - textureFormat: -1 - textureCompression: 1 - compressionQuality: 50 - crunchedCompression: 0 - allowsAlphaSplitting: 0 - overridden: 0 - ignorePlatformSupport: 0 - androidETC2FallbackOverride: 0 - forceMaximumCompressionQuality_BC6H_BC7: 0 - - serializedVersion: 3 - buildTarget: Server - maxTextureSize: 2048 - resizeAlgorithm: 0 - textureFormat: -1 - textureCompression: 1 - compressionQuality: 50 - crunchedCompression: 0 - allowsAlphaSplitting: 0 - overridden: 0 - ignorePlatformSupport: 0 - androidETC2FallbackOverride: 0 - forceMaximumCompressionQuality_BC6H_BC7: 0 - spriteSheet: - serializedVersion: 2 - sprites: [] - outline: [] - physicsShape: [] - bones: [] - spriteID: 5e97eb03825dee720800000000000000 - internalID: 0 - vertices: [] - indices: - edges: [] - weights: [] - secondaryTextures: [] - nameFileIdTable: {} - mipmapLimitGroupName: - pSDRemoveMatte: 0 - userData: - assetBundleName: - assetBundleVariant: diff --git a/Assets/UI/Art/签名字迹.png b/Assets/UI/Art/签名字迹.png new file mode 100644 index 000000000..e52f485d6 --- /dev/null +++ b/Assets/UI/Art/签名字迹.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:7b67ddf47ddadf9ef2d26dc1396805d2cd354a5a7aedafffa8f6d5f335642ddb +size 1310 diff --git a/Assets/UI/Art/Sign Here.png.meta b/Assets/UI/Art/签名字迹.png.meta similarity index 74% rename from Assets/UI/Art/Sign Here.png.meta rename to Assets/UI/Art/签名字迹.png.meta index 2e4ca01ed..1c13e18d2 100644 --- a/Assets/UI/Art/Sign Here.png.meta +++ b/Assets/UI/Art/签名字迹.png.meta @@ -1,5 +1,5 @@ fileFormatVersion: 2 -guid: e6463518763a87942833aad044b46e12 +guid: 4b7004709baa3f942998e7994b48bd38 TextureImporter: internalIDToNameTable: [] externalObjects: {} @@ -34,7 +34,7 @@ TextureImporter: maxTextureSize: 2048 textureSettings: serializedVersion: 2 - filterMode: 1 + filterMode: 0 aniso: 1 mipBias: 0 wrapU: 1 @@ -43,14 +43,14 @@ TextureImporter: nPOTScale: 0 lightmap: 0 compressionQuality: 50 - spriteMode: 1 + spriteMode: 2 spriteExtrude: 1 spriteMeshType: 1 alignment: 0 spritePivot: {x: 0.5, y: 0.5} spritePixelsToUnits: 100 spriteBorder: {x: 0, y: 0, z: 0, w: 0} - spriteGenerateFallbackPhysicsShape: 1 + spriteGenerateFallbackPhysicsShape: 0 alphaUsage: 1 alphaIsTransparency: 1 spriteTessellationDetail: -1 @@ -72,7 +72,7 @@ TextureImporter: maxTextureSize: 2048 resizeAlgorithm: 0 textureFormat: -1 - textureCompression: 1 + textureCompression: 0 compressionQuality: 50 crunchedCompression: 0 allowsAlphaSplitting: 0 @@ -134,7 +134,49 @@ TextureImporter: forceMaximumCompressionQuality_BC6H_BC7: 0 spriteSheet: serializedVersion: 2 - sprites: [] + sprites: + - serializedVersion: 2 + name: "\u6B64\u5904\u7B7E\u540D" + rect: + serializedVersion: 2 + x: 0 + y: 100 + width: 353 + height: 100 + alignment: 0 + pivot: {x: 0.5, y: 0.5} + border: {x: 0, y: 0, z: 0, w: 0} + outline: [] + physicsShape: [] + tessellationDetail: 0 + bones: [] + spriteID: 6f7d3c4ac39a7874eab3bdf4e377a4e4 + internalID: 1224447322 + vertices: [] + indices: + edges: [] + weights: [] + - serializedVersion: 2 + name: SignHere + rect: + serializedVersion: 2 + x: 0 + y: 0 + width: 353 + height: 100 + alignment: 0 + pivot: {x: 0.5, y: 0.5} + border: {x: 0, y: 0, z: 0, w: 0} + outline: [] + physicsShape: [] + tessellationDetail: 0 + bones: [] + spriteID: 16d7fe05d67e1e743a3304ab3ff46f48 + internalID: 2006242356 + vertices: [] + indices: + edges: [] + weights: [] outline: [] physicsShape: [] bones: [] @@ -145,7 +187,9 @@ TextureImporter: edges: [] weights: [] secondaryTextures: [] - nameFileIdTable: {} + nameFileIdTable: + SignHere: 2006242356 + "\u6B64\u5904\u7B7E\u540D": 1224447322 mipmapLimitGroupName: pSDRemoveMatte: 0 userData: diff --git a/Assets/UI/Metarials/CurtainReveal.mat b/Assets/UI/Metarials/CurtainReveal.mat new file mode 100644 index 000000000..ceedce7a7 --- /dev/null +++ b/Assets/UI/Metarials/CurtainReveal.mat @@ -0,0 +1,58 @@ +%YAML 1.1 +%TAG !u! tag:unity3d.com,2011: +--- !u!21 &2100000 +Material: + serializedVersion: 8 + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_Name: CurtainReveal + m_Shader: {fileID: 4800000, guid: bb01377b8e49b39439653b16c87b914e, type: 3} + m_Parent: {fileID: 0} + m_ModifiedSerializedProperties: 0 + m_ValidKeywords: [] + m_InvalidKeywords: [] + m_LightmapFlags: 4 + m_EnableInstancingVariants: 0 + m_DoubleSidedGI: 0 + m_CustomRenderQueue: -1 + stringTagMap: {} + disabledShaderPasses: [] + m_LockedProperties: + m_SavedProperties: + serializedVersion: 3 + m_TexEnvs: + - _AlphaTex: + m_Texture: {fileID: 0} + m_Scale: {x: 1, y: 1} + m_Offset: {x: 0, y: 0} + - _MainTex: + m_Texture: {fileID: 0} + m_Scale: {x: 1, y: 1} + m_Offset: {x: 0, y: 0} + - _MaskTex: + m_Texture: {fileID: 0} + m_Scale: {x: 1, y: 1} + m_Offset: {x: 0, y: 0} + - _NormalMap: + m_Texture: {fileID: 0} + m_Scale: {x: 1, y: 1} + m_Offset: {x: 0, y: 0} + m_Ints: [] + m_Floats: + - _ColorMask: 15 + - _EdgeSoftness: 0.02 + - _EnableExternalAlpha: 0 + - _RevealProgress: 0 + - _Stencil: 0 + - _StencilComp: 8 + - _StencilOp: 0 + - _StencilReadMask: 255 + - _StencilWriteMask: 255 + - _UseUIAlphaClip: 0 + m_Colors: + - _Color: {r: 1, g: 1, b: 1, a: 1} + - _Flip: {r: 1, g: 1, b: 1, a: 1} + - _RendererColor: {r: 1, g: 1, b: 1, a: 1} + m_BuildTextureStacks: [] diff --git a/Assets/UI/Metarials/CurtainReveal.mat.meta b/Assets/UI/Metarials/CurtainReveal.mat.meta new file mode 100644 index 000000000..a2493384f --- /dev/null +++ b/Assets/UI/Metarials/CurtainReveal.mat.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 0ae8d967cfb953c45a72649c19320118 +NativeFormatImporter: + externalObjects: {} + mainObjectFileID: 2100000 + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/UI/Shaders.meta b/Assets/UI/Shaders.meta new file mode 100644 index 000000000..318a2ea08 --- /dev/null +++ b/Assets/UI/Shaders.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 91c05e37ce21271478c4eb393a065465 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/UI/Shaders/CurtainReveal.shader b/Assets/UI/Shaders/CurtainReveal.shader new file mode 100644 index 000000000..57e2a9fac --- /dev/null +++ b/Assets/UI/Shaders/CurtainReveal.shader @@ -0,0 +1,132 @@ +Shader "UI/CurtainReveal" +{ + Properties + { + [PerRendererData] _MainTex ("Sprite Texture", 2D) = "white" {} + _Color ("Tint", Color) = (1,1,1,1) + + [Header(Curtain Reveal)] + _RevealProgress ("Reveal Progress", Range(0, 1)) = 0 + _EdgeSoftness ("Edge Softness", Range(0, 0.1)) = 0.02 + + _StencilComp ("Stencil Comparison", Float) = 8 + _Stencil ("Stencil ID", Float) = 0 + _StencilOp ("Stencil Operation", Float) = 0 + _StencilWriteMask ("Stencil Write Mask", Float) = 255 + _StencilReadMask ("Stencil Read Mask", Float) = 255 + _ColorMask ("Color Mask", Float) = 15 + + [Toggle(UNITY_UI_ALPHACLIP)] _UseUIAlphaClip ("Use Alpha Clip", Float) = 0 + } + + SubShader + { + Tags + { + "Queue"="Transparent" + "IgnoreProjector"="True" + "RenderType"="Transparent" + "PreviewType"="Plane" + "CanUseSpriteAtlas"="True" + } + + Stencil + { + Ref [_Stencil] + Comp [_StencilComp] + Pass [_StencilOp] + ReadMask [_StencilReadMask] + WriteMask [_StencilWriteMask] + } + + Cull Off + Lighting Off + ZWrite Off + ZTest [unity_GUIZTestMode] + Blend SrcAlpha OneMinusSrcAlpha + ColorMask [_ColorMask] + + Pass + { + Name "Default" + CGPROGRAM + #pragma vertex vert + #pragma fragment frag + #pragma target 2.0 + + #include "UnityCG.cginc" + #include "UnityUI.cginc" + + #pragma multi_compile_local _ UNITY_UI_ALPHACLIP + + struct appdata_t + { + float4 vertex : POSITION; + float4 color : COLOR; + float2 texcoord : TEXCOORD0; + UNITY_VERTEX_INPUT_INSTANCE_ID + }; + + struct v2f + { + float4 vertex : SV_POSITION; + fixed4 color : COLOR; + float2 texcoord : TEXCOORD0; + float4 worldPosition : TEXCOORD1; + UNITY_VERTEX_OUTPUT_STEREO + }; + + sampler2D _MainTex; + fixed4 _Color; + fixed4 _TextureSampleAdd; + float4 _ClipRect; + float4 _MainTex_ST; + + float _RevealProgress; + float _EdgeSoftness; + + v2f vert(appdata_t v) + { + v2f OUT; + UNITY_SETUP_INSTANCE_ID(v); + UNITY_INITIALIZE_VERTEX_OUTPUT_STEREO(OUT); + OUT.worldPosition = v.vertex; + OUT.vertex = UnityObjectToClipPos(OUT.worldPosition); + + OUT.texcoord = TRANSFORM_TEX(v.texcoord, _MainTex); + + OUT.color = v.color * _Color; + return OUT; + } + + fixed4 frag(v2f IN) : SV_Target + { + half4 color = (tex2D(_MainTex, IN.texcoord) + _TextureSampleAdd) * IN.color; + + // 计算当前UV的X坐标(0到1) + float uvX = IN.texcoord.x; + + // 窗帘展开效果:从左到右 + // 如果UV的X坐标大于进度值,则裁剪掉 + float reveal = step(uvX, _RevealProgress); + + // 添加边缘柔化效果 + float edgeStart = _RevealProgress - _EdgeSoftness; + float edgeEnd = _RevealProgress; + float edgeFactor = smoothstep(edgeStart, edgeEnd, uvX); + reveal = lerp(reveal, 0, edgeFactor); + + // 应用裁剪 + color.a *= reveal; + + #ifdef UNITY_UI_ALPHACLIP + clip (color.a - 0.001); + #endif + + return color; + } + ENDCG + } + } +} + diff --git a/Assets/UI/Shaders/CurtainReveal.shader.meta b/Assets/UI/Shaders/CurtainReveal.shader.meta new file mode 100644 index 000000000..7e2cc9757 --- /dev/null +++ b/Assets/UI/Shaders/CurtainReveal.shader.meta @@ -0,0 +1,9 @@ +fileFormatVersion: 2 +guid: bb01377b8e49b39439653b16c87b914e +ShaderImporter: + externalObjects: {} + defaultTextures: [] + nonModifiableTextures: [] + userData: + assetBundleName: + assetBundleVariant: