diff --git a/Assets/Scenes/PeipeiFixScene.unity b/Assets/Scenes/PeipeiFixScene.unity
index f9aaf0d03..9d56c98e5 100644
--- a/Assets/Scenes/PeipeiFixScene.unity
+++ b/Assets/Scenes/PeipeiFixScene.unity
@@ -10337,15 +10337,15 @@ SpriteRenderer:
m_SortingLayerID: 1034535805
m_SortingLayer: 9
m_SortingOrder: 100
- m_Sprite: {fileID: 4684698178741017078, guid: 043026da884677749bd2b9a2ecf10c79, type: 3}
+ m_Sprite: {fileID: -6067141311677397656, guid: fbc29369d0505384cadd5488b4933d4e, type: 3}
m_Color: {r: 1, g: 1, b: 1, a: 0}
m_FlipX: 0
m_FlipY: 0
m_DrawMode: 0
- m_Size: {x: 5.3348837, y: 9.2}
+ m_Size: {x: 6.837209, y: 9.232558}
m_AdaptiveModeThreshold: 0.5
m_SpriteTileMode: 0
- m_WasSpriteAssigned: 0
+ m_WasSpriteAssigned: 1
m_MaskInteraction: 0
m_SpriteSortPoint: 0
--- !u!114 &1261507409
diff --git a/Assets/Scripts/MiniGame/Peipei/UF/Editor.meta b/Assets/Scripts/MiniGame/Peipei/UF/Editor.meta
new file mode 100644
index 000000000..8bce287bb
--- /dev/null
+++ b/Assets/Scripts/MiniGame/Peipei/UF/Editor.meta
@@ -0,0 +1,8 @@
+fileFormatVersion: 2
+guid: abb5f01e5e4a41a40b87f06d776d61cb
+folderAsset: yes
+DefaultImporter:
+ externalObjects: {}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/Assets/Scripts/MiniGame/Peipei/UF/Editor/FixPeipeiUfFaceSprite.cs b/Assets/Scripts/MiniGame/Peipei/UF/Editor/FixPeipeiUfFaceSprite.cs
new file mode 100644
index 000000000..616ba6674
--- /dev/null
+++ b/Assets/Scripts/MiniGame/Peipei/UF/Editor/FixPeipeiUfFaceSprite.cs
@@ -0,0 +1,143 @@
+using System.Linq;
+using UnityEditor;
+using UnityEditor.SceneManagement;
+using UnityEngine;
+
+namespace AibisDream.EditorTools
+{
+ ///
+ /// PP2 通用动画 aseprite 重导入后,PeipeiFixScene 里 UF_face(垂手)若仍指向旧子资源 ID 会丢图。
+ /// 本工具改绑到「佩佩像素版-默认」aseprite 的 Frame_0。
+ ///
+ public static class FixPeipeiUfFaceSprite
+ {
+ private const string ScenePath = "Assets/Scenes/PeipeiFixScene.unity";
+ // 垂手姿态用「默认」aseprite,避免依赖被 PP2 重导过的通用动画子资源 ID。
+ private const string AsepritePath =
+ "Assets/RawResources/Actor/\u4f69\u4f69/\u4f69\u4f69\u50cf\u7d20\u7248-\u9ed8\u8ba4.aseprite";
+ private const string FaceObjectName = "\u5782\u624b (1)"; // 垂手 (1)
+
+ [MenuItem("AIBIS/Fix/Rebind Peipei UF Face Sprite")]
+ public static void RebindFromMenu()
+ {
+ if (!Rebind(out var message))
+ {
+ EditorUtility.DisplayDialog("Rebind UF Face", message, "OK");
+ return;
+ }
+
+ EditorUtility.DisplayDialog("Rebind UF Face", message, "OK");
+ }
+
+ /// Unity batchmode: -executeMethod AibisDream.EditorTools.FixPeipeiUfFaceSprite.RebindBatch
+ public static void RebindBatch()
+ {
+ if (!Rebind(out var message))
+ {
+ Debug.LogError("[FixPeipeiUfFaceSprite] " + message);
+ EditorApplication.Exit(1);
+ return;
+ }
+
+ Debug.Log("[FixPeipeiUfFaceSprite] " + message);
+ EditorApplication.Exit(0);
+ }
+
+ private static bool Rebind(out string message)
+ {
+ var sprites = AssetDatabase.LoadAllAssetsAtPath(AsepritePath)
+ .OfType()
+ .ToArray();
+ if (sprites.Length == 0)
+ {
+ message = "No sprites found in: " + AsepritePath;
+ return false;
+ }
+
+ var sprite = sprites.FirstOrDefault(s => s != null && s.name.Contains("Frame_0"))
+ ?? sprites.FirstOrDefault(s => s != null);
+ if (sprite == null)
+ {
+ message = "Could not pick a sprite from aseprite.";
+ return false;
+ }
+
+ var sceneAlreadyOpen = false;
+ for (var i = 0; i < EditorSceneManager.sceneCount; i++)
+ {
+ if (EditorSceneManager.GetSceneAt(i).path == ScenePath)
+ {
+ sceneAlreadyOpen = true;
+ break;
+ }
+ }
+
+ var scene = sceneAlreadyOpen
+ ? EditorSceneManager.GetSceneByPath(ScenePath)
+ : EditorSceneManager.OpenScene(ScenePath, OpenSceneMode.Additive);
+
+ GameObject faceGo = null;
+ foreach (var root in scene.GetRootGameObjects())
+ {
+ foreach (var t in root.GetComponentsInChildren(true))
+ {
+ if (t.gameObject.name == FaceObjectName)
+ {
+ faceGo = t.gameObject;
+ break;
+ }
+ }
+
+ if (faceGo != null)
+ {
+ break;
+ }
+ }
+
+ if (faceGo == null)
+ {
+ if (!sceneAlreadyOpen)
+ {
+ EditorSceneManager.CloseScene(scene, true);
+ }
+
+ message = "Face object not found: " + FaceObjectName;
+ return false;
+ }
+
+ var renderer = faceGo.GetComponent();
+ if (renderer == null)
+ {
+ if (!sceneAlreadyOpen)
+ {
+ EditorSceneManager.CloseScene(scene, true);
+ }
+
+ message = "SpriteRenderer missing on " + FaceObjectName;
+ return false;
+ }
+
+ Undo.RecordObject(renderer, "Rebind UF Face Sprite");
+ renderer.sprite = sprite;
+ EditorUtility.SetDirty(renderer);
+ EditorSceneManager.MarkSceneDirty(scene);
+ EditorSceneManager.SaveScene(scene);
+
+ if (!sceneAlreadyOpen)
+ {
+ EditorSceneManager.CloseScene(scene, true);
+ }
+
+ AssetDatabase.TryGetGUIDAndLocalFileIdentifier(
+ (Object)sprite, out var guid, out long localId);
+
+ message = string.Format(
+ "Rebound {0} -> {1} (guid={2} fileID={3})",
+ FaceObjectName,
+ sprite.name,
+ guid,
+ localId);
+ return true;
+ }
+ }
+}
diff --git a/Assets/Scripts/MiniGame/Peipei/UF/Editor/FixPeipeiUfFaceSprite.cs.meta b/Assets/Scripts/MiniGame/Peipei/UF/Editor/FixPeipeiUfFaceSprite.cs.meta
new file mode 100644
index 000000000..e2e226486
--- /dev/null
+++ b/Assets/Scripts/MiniGame/Peipei/UF/Editor/FixPeipeiUfFaceSprite.cs.meta
@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: f988c46355a846d4daeb2b647a500c82
+MonoImporter:
+ externalObjects: {}
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: 0
+ icon: {instanceID: 0}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/Assets/Scripts/MiniGame/Peipei/UF/UFSystem.cs b/Assets/Scripts/MiniGame/Peipei/UF/UFSystem.cs
index 8b4082d0a..c601e51dc 100644
--- a/Assets/Scripts/MiniGame/Peipei/UF/UFSystem.cs
+++ b/Assets/Scripts/MiniGame/Peipei/UF/UFSystem.cs
@@ -135,6 +135,17 @@ public class UFSystem : MonoBehaviour
[YarnCommand("UF_faceIn")]
public IEnumerator UF_faceIn(float duration = 1.5f)
{
+ if (face == null)
+ {
+ Debug.LogWarning("[UFSystem] UF_faceIn: face SpriteRenderer 未绑定。", this);
+ yield break;
+ }
+
+ if (face.sprite == null)
+ {
+ Debug.LogWarning("[UFSystem] UF_faceIn: face.sprite 为空(可能是 aseprite 重导入后子资源 ID 失效)。", this);
+ }
+
face.gameObject.SetActive(true);
face.DOFade(1f, duration);
yield return new WaitForSeconds(duration);
@@ -143,6 +154,11 @@ public class UFSystem : MonoBehaviour
[YarnCommand("UF_faceOut")]
public IEnumerator UF_faceOut(float duration = 1.5f)
{
+ if (face == null)
+ {
+ yield break;
+ }
+
face.DOFade(0f, duration);
yield return new WaitForSeconds(duration);
face.gameObject.SetActive(false);
@@ -290,6 +306,8 @@ public class UFSystem : MonoBehaviour
public void OpenUFView()
{
ClearUnifiedUfSpriteWeights();
+ // 先于 EyeSystem.OnEnter:UF 段 SwitchState / sprite 极密,禁止 autoBlink 与过渡眨眼
+ eyeSystem?.SetNonExplicitBlinkSuppressed(true);
if (FullAscillVolume != null && !FullAscillVolume.gameObject.activeInHierarchy)
{
@@ -335,6 +353,7 @@ public class UFSystem : MonoBehaviour
}
ClearUnifiedUfSpriteWeights();
+ eyeSystem?.SetNonExplicitBlinkSuppressed(false);
}
public void OpenCover()