签字功能完成

This commit is contained in:
2025-11-19 15:08:08 +08:00
parent 3f328c1b74
commit cf1d020b99
22 changed files with 837 additions and 284 deletions
@@ -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:
@@ -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
+4
View File
@@ -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: []
@@ -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: []
+132 -74
View File
@@ -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
@@ -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<PlayToolPanel>();
panel.ShowSignBoard();
if (enable)
{
panel.EnableSignBoard();
}
}
[YarnCommand("enable_sign_board")]
public static void EnableSignBoard()
{
var panel = UIManager.Instance.GetPanel<PlayToolPanel>();
panel.EnableSignBoard();
}
[YarnCommand("close_sign_board")]
public static void CloseSignBoard()
{
var panel = UIManager.Instance.GetPanel<PlayToolPanel>();
panel.CloseSignBoard();
}
#endregion
}
}
+4 -1
View File
@@ -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
@@ -0,0 +1,154 @@
using System.Collections;
using DG.Tweening;
using UnityEngine;
using UnityEngine.UI;
namespace AibisDream.Framework
{
/// <summary>
/// UI Image窗帘展开效果工具类(使用Shader实现)
/// 实现从左到右的展开动画,保持图片原始宽高比不被压扁
/// </summary>
public static class CurtainRevealKit
{
private static readonly int RevealProgressPropertyID = Shader.PropertyToID("_RevealProgress");
private static readonly int EdgeSoftnessPropertyID = Shader.PropertyToID("_EdgeSoftness");
/// <summary>
/// 从左到右显示图片(窗帘拉开效果),使用Shader实现,保持图片原始宽高比
/// </summary>
/// <param name="image">目标Image组件</param>
/// <param name="duration">动画时长</param>
/// <param name="easeType">缓动类型</param>
/// <param name="edgeSoftness">边缘柔化程度(0-0.1</param>
/// <returns>协程</returns>
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();
}
/// <summary>
/// 同步版本:从左到右显示图片
/// </summary>
/// <param name="image">目标Image组件</param>
/// <param name="duration">动画时长</param>
/// <param name="easeType">缓动类型</param>
/// <param name="edgeSoftness">边缘柔化程度(0-0.1</param>
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);
}
/// <summary>
/// 设置窗帘展开进度(手动控制)
/// </summary>
/// <param name="image">目标Image组件</param>
/// <param name="progress">进度值(0-1</param>
/// <param name="edgeSoftness">边缘柔化程度(0-0.1</param>
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));
}
/// <summary>
/// 重置窗帘展开效果(恢复原始状态)
/// </summary>
/// <param name="image">目标Image组件</param>
public static void ResetReveal(this Image image)
{
if (image == null || image.material == null) return;
Material material = EnsureMaterialInstance(image);
material.SetFloat(RevealProgressPropertyID, 1f);
}
/// <summary>
/// 确保Material有独立实例(避免影响其他对象)
/// </summary>
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;
}
}
}
@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 5fdfd099f63f488468ea20b150b8e883
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
+17
View File
@@ -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<ModalWindowProgressBar>();
_redFlash = transform.Find("Red Flash").GetComponent<Image>();
_warningWindow = transform.Find("警告窗口").GetComponent<ModalWindow>();
_signBoard = transform.GetComponentInChildren<SignBoard>();
}
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);
+165 -40
View File
@@ -1,9 +1,11 @@
using System;
using UnityEngine;
using UnityEngine.Events;
using UnityEngine.EventSystems;
using UnityEngine.UI;
namespace AibisDream
{
{
/// <summary>
/// 画板组件,支持鼠标拖动绘制(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<float> 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<RawImage>();
rectTransform = GetComponent<RectTransform>();
}
private void Start()
{
InitializeTexture();
}
/// <summary>
/// 初始化画布纹理
/// </summary>
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;
}
/// <summary>
@@ -94,38 +120,41 @@ namespace AibisDream
}
/// <summary>
/// 在指定位置绘制
/// 在指定位置绘制(方形像素笔刷)
/// </summary>
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
/// </summary>
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;
}
/// <summary>
@@ -201,11 +241,54 @@ namespace AibisDream
backgroundColor = color;
}
/// <summary>
/// 设置目标绘画长度
/// </summary>
public void SetTargetLength(float length)
{
targetLength = Mathf.Max(0f, length);
}
/// <summary>
/// 获取累计所有笔画的总长度
/// </summary>
public float GetCurrentStrokeLength()
{
return totalLength;
}
/// <summary>
/// 设置锁定状态
/// </summary>
/// <param name="locked">true 为锁定(无法绘画),false 为解锁(可以绘画)</param>
public void SetLocked(bool locked)
{
isLocked = locked;
// 如果锁定,停止当前绘画
if (locked && isDrawing)
{
isDrawing = false;
}
}
/// <summary>
/// 获取当前锁定状态
/// </summary>
public bool IsLocked()
{
return isLocked;
}
/// <summary>
/// 保存画布为PNG图片
/// </summary>
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;
}
+37 -3
View File
@@ -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));
}
}
}
Binary file not shown.
Binary file not shown.
-153
View File
@@ -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:
Binary file not shown.
@@ -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:
+58
View File
@@ -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: []
@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 0ae8d967cfb953c45a72649c19320118
NativeFormatImporter:
externalObjects: {}
mainObjectFileID: 2100000
userData:
assetBundleName:
assetBundleVariant:
+8
View File
@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 91c05e37ce21271478c4eb393a065465
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:
+132
View File
@@ -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
}
}
}
@@ -0,0 +1,9 @@
fileFormatVersion: 2
guid: bb01377b8e49b39439653b16c87b914e
ShaderImporter:
externalObjects: {}
defaultTextures: []
nonModifiableTextures: []
userData:
assetBundleName:
assetBundleVariant: