fix(peipei-uf): 重绑垂手脸图并加固faceIn

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
2026-07-21 01:19:16 +08:00
co-authored by Cursor
parent 1e8ec2e64f
commit 5f31cd5635
5 changed files with 184 additions and 3 deletions
+3 -3
View File
@@ -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
@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: abb5f01e5e4a41a40b87f06d776d61cb
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,143 @@
using System.Linq;
using UnityEditor;
using UnityEditor.SceneManagement;
using UnityEngine;
namespace AibisDream.EditorTools
{
/// <summary>
/// PP2 通用动画 aseprite 重导入后,PeipeiFixScene 里 UF_face(垂手)若仍指向旧子资源 ID 会丢图。
/// 本工具改绑到「佩佩像素版-默认」aseprite 的 Frame_0。
/// </summary>
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");
}
/// <summary>Unity batchmode: -executeMethod AibisDream.EditorTools.FixPeipeiUfFaceSprite.RebindBatch</summary>
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<Sprite>()
.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<Transform>(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<SpriteRenderer>();
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;
}
}
}
@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: f988c46355a846d4daeb2b647a500c82
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
@@ -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.OnEnterUF 段 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()