From 4739e14d8d0c51f220f16667bab4c1b081e71807 Mon Sep 17 00:00:00 2001 From: Ding Yuntian <1491671119@qq.com> Date: Sun, 2 Aug 2026 19:49:22 +0800 Subject: [PATCH 1/4] =?UTF-8?q?fix:=20=E4=BD=A9=E4=BD=A9=E5=A4=96=E5=87=BA?= =?UTF-8?q?=E5=8A=A8=E7=94=BB=E6=9B=BF=E6=8D=A2?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Scene_ClincOut/Actor/佩佩/外出佩佩.asset | 4 +- .../Actor/佩佩/外出佩佩动画.aseprite | Bin 16817 -> 16817 bytes .../Actor/佩佩/外出佩佩动画.aseprite.meta | 64 +++++++++--------- .../Actor/佩佩/外出佩佩动画.png | 4 +- .../Actor/佩佩/外出佩佩动画.png.meta | 22 +++--- 5 files changed, 47 insertions(+), 47 deletions(-) diff --git a/Assets/GameContent/Scene_ClincOut/Actor/佩佩/外出佩佩.asset b/Assets/GameContent/Scene_ClincOut/Actor/佩佩/外出佩佩.asset index dd1401594..514ee6492 100644 --- a/Assets/GameContent/Scene_ClincOut/Actor/佩佩/外出佩佩.asset +++ b/Assets/GameContent/Scene_ClincOut/Actor/佩佩/外出佩佩.asset @@ -102,7 +102,7 @@ MonoBehaviour: pivot: {x: 0.5, y: 0} manageSpriteSlicing: 1 defaultNewClipEndBehavior: 1 - lastSourceHash: 485ca4cd836da53956fa933f659d69cf255a9c63431dccadca0482ae278e1267 + lastSourceHash: 50bd6a59682417d8b5d78f8f5cdb37c5c361d0f4aee2ef0699b738ab832d8984 - internalId: aa50bff678794da7b1e5a1fb4dc05aeb displayName: "\u63A5\u843D\u53F6" isEnabled: 1 @@ -111,7 +111,7 @@ MonoBehaviour: pivot: {x: 0.5, y: 0} manageSpriteSlicing: 1 defaultNewClipEndBehavior: 0 - lastSourceHash: 5fd8e2b9a717a4a94e0f2189df5d1b4bdb3505598be66bd28894a0e155089fd8 + lastSourceHash: 1636ab2c65e071d08616fbe2e89b7f084f1f3d15d2448aac4c3d7c2bad6c654f settings: defaultPlayableId: HOODBACK newManualClipDefaultEndBehavior: 0 diff --git a/Assets/GameContent/Scene_ClincOut/Actor/佩佩/外出佩佩动画.aseprite b/Assets/GameContent/Scene_ClincOut/Actor/佩佩/外出佩佩动画.aseprite index 386ad3310285072df1476594586091917b6f0bf4..a77ecc0dc96f763038226860c63a81419c7640f0 100644 GIT binary patch delta 149 zcmdnk%($_cQFx;x1H*%*91P(M-3$s0j0_A6Y7>P^8Pzs+-e+NC+MLN6z%H!D!~m92 zU}s=pRbXUbVqn^QhDR15!G$F8N0=QUfvQ84eX_Qa6eH(mH>IcSKuSmJ9*E*LdI6%o ISng#90N3&xPXGV_ delta 149 zcmdnk%($_cQFx;x1H*%*91P(M-3$s0j0_A6dnO8(GVa;fd7p)madReX0K4!WCI+yK z0y_f(s{$hf69ePsGd!{g2`(gwKf>$?2~-`T?31;Xq!>9iyD2?o2U0p(_dpc4(F+ju I#d0q@0P|HIH2?qr diff --git a/Assets/GameContent/Scene_ClincOut/Actor/佩佩/外出佩佩动画.aseprite.meta b/Assets/GameContent/Scene_ClincOut/Actor/佩佩/外出佩佩动画.aseprite.meta index 68b48af40..0284c5315 100644 --- a/Assets/GameContent/Scene_ClincOut/Actor/佩佩/外出佩佩动画.aseprite.meta +++ b/Assets/GameContent/Scene_ClincOut/Actor/佩佩/外出佩佩动画.aseprite.meta @@ -121,7 +121,7 @@ ScriptedImporter: animatedSpriteImportData: - name: "\u5916\u51FA\u4F69\u4F69\u52A8\u753B_Frame_4" originalName: - pivot: {x: 0.4940476, y: 0.02962963} + pivot: {x: 0.4940476, y: 0.037037037} alignment: 9 border: {x: 0, y: 0, z: 0, w: 0} rect: @@ -130,7 +130,7 @@ ScriptedImporter: y: 154 width: 84 height: 135 - spriteID: ae301e51cf8f70c41a65b6bcd8ffa7fe + spriteID: f65d4e6e709a93a498f8d3b02087a8c6 spriteBone: [] spriteOutline: [] vertices: [] @@ -141,7 +141,7 @@ ScriptedImporter: uvTransform: {x: 96, y: 154} - name: "\u5916\u51FA\u4F69\u4F69\u52A8\u753B_Frame_5" originalName: - pivot: {x: 0.4940476, y: 0.02962963} + pivot: {x: 0.4940476, y: 0.037037037} alignment: 9 border: {x: 0, y: 0, z: 0, w: 0} rect: @@ -150,7 +150,7 @@ ScriptedImporter: y: 297 width: 84 height: 135 - spriteID: 24194db597149de48afe47f86e3d716a + spriteID: 1b74b2874cbe5a746a16e6a4019e4977 spriteBone: [] spriteOutline: [] vertices: [] @@ -161,7 +161,7 @@ ScriptedImporter: uvTransform: {x: 96, y: 297} - name: "\u5916\u51FA\u4F69\u4F69\u52A8\u753B_Frame_6" originalName: - pivot: {x: 0.4940476, y: 0.02962963} + pivot: {x: 0.4940476, y: 0.037037037} alignment: 9 border: {x: 0, y: 0, z: 0, w: 0} rect: @@ -170,7 +170,7 @@ ScriptedImporter: y: 4 width: 84 height: 135 - spriteID: b1e9b9a7a1442dc4dbb431998d003626 + spriteID: bd08a4f68cf6a01478b589cea287e499 spriteBone: [] spriteOutline: [] vertices: [] @@ -181,7 +181,7 @@ ScriptedImporter: uvTransform: {x: 188, y: 4} - name: "\u5916\u51FA\u4F69\u4F69\u52A8\u753B_Frame_7" originalName: - pivot: {x: 0.4940476, y: 0.02962963} + pivot: {x: 0.4940476, y: 0.037037037} alignment: 9 border: {x: 0, y: 0, z: 0, w: 0} rect: @@ -190,7 +190,7 @@ ScriptedImporter: y: 147 width: 84 height: 135 - spriteID: 03467ff8ee7aefd439baefa64a9ff0cd + spriteID: a077f61c138da694391a65ef78c5a607 spriteBone: [] spriteOutline: [] vertices: [] @@ -201,7 +201,7 @@ ScriptedImporter: uvTransform: {x: 188, y: 147} - name: "\u5916\u51FA\u4F69\u4F69\u52A8\u753B_Frame_0" originalName: - pivot: {x: 0.4940476, y: 0.028169015} + pivot: {x: 0.4940476, y: 0.03521127} alignment: 9 border: {x: 0, y: 0, z: 0, w: 0} rect: @@ -210,7 +210,7 @@ ScriptedImporter: y: 4 width: 84 height: 142 - spriteID: b688d53adb03ed54eb4520c6a23f8e1b + spriteID: c1c836b84f6769c4d9f9184f0fd618a7 spriteBone: [] spriteOutline: [] vertices: [] @@ -221,7 +221,7 @@ ScriptedImporter: uvTransform: {x: 4, y: 4} - name: "\u5916\u51FA\u4F69\u4F69\u52A8\u753B_Frame_1" originalName: - pivot: {x: 0.4940476, y: 0.028169015} + pivot: {x: 0.4940476, y: 0.03521127} alignment: 9 border: {x: 0, y: 0, z: 0, w: 0} rect: @@ -230,7 +230,7 @@ ScriptedImporter: y: 154 width: 84 height: 142 - spriteID: 10bb7ea29d086ba4a96bcfe62815f55a + spriteID: 0a6d075d6be7b784595940436a0b4fb3 spriteBone: [] spriteOutline: [] vertices: [] @@ -241,7 +241,7 @@ ScriptedImporter: uvTransform: {x: 4, y: 154} - name: "\u5916\u51FA\u4F69\u4F69\u52A8\u753B_Frame_2" originalName: - pivot: {x: 0.4940476, y: 0.028169015} + pivot: {x: 0.4940476, y: 0.03521127} alignment: 9 border: {x: 0, y: 0, z: 0, w: 0} rect: @@ -250,7 +250,7 @@ ScriptedImporter: y: 304 width: 84 height: 142 - spriteID: 4c69fbb003a3c934f81427886b342a19 + spriteID: ec3e6cf37017e42498d4ab28a33bc5c7 spriteBone: [] spriteOutline: [] vertices: [] @@ -261,7 +261,7 @@ ScriptedImporter: uvTransform: {x: 4, y: 304} - name: "\u5916\u51FA\u4F69\u4F69\u52A8\u753B_Frame_3" originalName: - pivot: {x: 0.4940476, y: 0.028169015} + pivot: {x: 0.4940476, y: 0.03521127} alignment: 9 border: {x: 0, y: 0, z: 0, w: 0} rect: @@ -270,7 +270,7 @@ ScriptedImporter: y: 4 width: 84 height: 142 - spriteID: 12bacbb24a07fce4e9eb9575ade58eb6 + spriteID: 835ea04ae6c66864c955bf66d3219e07 spriteBone: [] spriteOutline: [] vertices: [] @@ -292,66 +292,66 @@ ScriptedImporter: frameIndex: 4 cellRect: x: 2 - y: -4 + y: -5 width: 84 height: 135 - spriteId: ae301e51cf8f70c41a65b6bcd8ffa7fe + spriteId: f65d4e6e709a93a498f8d3b02087a8c6 - name: "\u5916\u51FA\u4F69\u4F69\u52A8\u753B_Frame_5" frameIndex: 5 cellRect: x: 2 - y: -4 + y: -5 width: 84 height: 135 - spriteId: 24194db597149de48afe47f86e3d716a + spriteId: 1b74b2874cbe5a746a16e6a4019e4977 - name: "\u5916\u51FA\u4F69\u4F69\u52A8\u753B_Frame_6" frameIndex: 6 cellRect: x: 2 - y: -4 + y: -5 width: 84 height: 135 - spriteId: b1e9b9a7a1442dc4dbb431998d003626 + spriteId: bd08a4f68cf6a01478b589cea287e499 - name: "\u5916\u51FA\u4F69\u4F69\u52A8\u753B_Frame_7" frameIndex: 7 cellRect: x: 2 - y: -4 + y: -5 width: 84 height: 135 - spriteId: 03467ff8ee7aefd439baefa64a9ff0cd + spriteId: a077f61c138da694391a65ef78c5a607 - name: "\u5916\u51FA\u4F69\u4F69\u52A8\u753B_Frame_0" frameIndex: 0 cellRect: x: 2 - y: -4 + y: -5 width: 84 height: 142 - spriteId: b688d53adb03ed54eb4520c6a23f8e1b + spriteId: c1c836b84f6769c4d9f9184f0fd618a7 - name: "\u5916\u51FA\u4F69\u4F69\u52A8\u753B_Frame_1" frameIndex: 1 cellRect: x: 2 - y: -4 + y: -5 width: 84 height: 142 - spriteId: 10bb7ea29d086ba4a96bcfe62815f55a + spriteId: 0a6d075d6be7b784595940436a0b4fb3 - name: "\u5916\u51FA\u4F69\u4F69\u52A8\u753B_Frame_2" frameIndex: 2 cellRect: x: 2 - y: -4 + y: -5 width: 84 height: 142 - spriteId: 4c69fbb003a3c934f81427886b342a19 + spriteId: ec3e6cf37017e42498d4ab28a33bc5c7 - name: "\u5916\u51FA\u4F69\u4F69\u52A8\u753B_Frame_3" frameIndex: 3 cellRect: x: 2 - y: -4 + y: -5 width: 84 height: 142 - spriteId: 12bacbb24a07fce4e9eb9575ade58eb6 + spriteId: 835ea04ae6c66864c955bf66d3219e07 linkedCells: [] parentIndex: -1 platformSettings: [] diff --git a/Assets/GameContent/Scene_ClincOut/Actor/佩佩/外出佩佩动画.png b/Assets/GameContent/Scene_ClincOut/Actor/佩佩/外出佩佩动画.png index b921b25b1..b2319d7c6 100644 --- a/Assets/GameContent/Scene_ClincOut/Actor/佩佩/外出佩佩动画.png +++ b/Assets/GameContent/Scene_ClincOut/Actor/佩佩/外出佩佩动画.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:4c3bd9937b373198d647ecfbe03d5e2b3d4e03f8f3e82544087f427f981b7d13 -size 13002 +oid sha256:81eee53ad4d9b61a51365915b08a59575c26a67fd3849aeea591839fe6c9c85d +size 12797 diff --git a/Assets/GameContent/Scene_ClincOut/Actor/佩佩/外出佩佩动画.png.meta b/Assets/GameContent/Scene_ClincOut/Actor/佩佩/外出佩佩动画.png.meta index 41a0ba240..0c85d0396 100644 --- a/Assets/GameContent/Scene_ClincOut/Actor/佩佩/外出佩佩动画.png.meta +++ b/Assets/GameContent/Scene_ClincOut/Actor/佩佩/外出佩佩动画.png.meta @@ -34,7 +34,7 @@ TextureImporter: maxTextureSize: 2048 textureSettings: serializedVersion: 2 - filterMode: 0 + filterMode: 1 aniso: 1 mipBias: 0 wrapU: 1 @@ -48,7 +48,7 @@ TextureImporter: spriteMeshType: 1 alignment: 0 spritePivot: {x: 0.5, y: 0.5} - spritePixelsToUnits: 43 + spritePixelsToUnits: 100 spriteBorder: {x: 0, y: 0, z: 0, w: 0} spriteGenerateFallbackPhysicsShape: 1 alphaUsage: 1 @@ -69,10 +69,10 @@ TextureImporter: platformSettings: - serializedVersion: 3 buildTarget: DefaultTexturePlatform - maxTextureSize: 512 + maxTextureSize: 2048 resizeAlgorithm: 0 textureFormat: -1 - textureCompression: 0 + textureCompression: 1 compressionQuality: 50 crunchedCompression: 0 allowsAlphaSplitting: 0 @@ -163,7 +163,7 @@ TextureImporter: physicsShape: [] tessellationDetail: 0 bones: [] - spriteID: ae9f812a7facb6943801c5f997dc8791 + spriteID: 2f4f3acf41159b54ab769a4f494269bf internalID: -5480261478056054798 vertices: [] indices: @@ -184,7 +184,7 @@ TextureImporter: physicsShape: [] tessellationDetail: 0 bones: [] - spriteID: ed6b480aec8ca1943925008bfe926f47 + spriteID: e7358aa0892b61e499f0bad12872aa0f internalID: -3638042381185026470 vertices: [] indices: @@ -205,7 +205,7 @@ TextureImporter: physicsShape: [] tessellationDetail: 0 bones: [] - spriteID: 4c2172b9fff780240bd26b7b41307a8c + spriteID: ecf6a1a15056159409323d0fcab2b313 internalID: 6156124535048162135 vertices: [] indices: @@ -226,7 +226,7 @@ TextureImporter: physicsShape: [] tessellationDetail: 0 bones: [] - spriteID: e7d63d04f1813f84484d356e8bfec6b8 + spriteID: 70004ccddf10b384ea9d428cf7f351d0 internalID: 2675976796409045425 vertices: [] indices: @@ -247,7 +247,7 @@ TextureImporter: physicsShape: [] tessellationDetail: 0 bones: [] - spriteID: 8d400c0f84910ff41a16e86ec781419a + spriteID: a016c9d212dbd3f468db3829f25e22d5 internalID: -7488143331455555456 vertices: [] indices: @@ -268,7 +268,7 @@ TextureImporter: physicsShape: [] tessellationDetail: 0 bones: [] - spriteID: 27d0bf2648a9d974a8fc0240f75e618d + spriteID: 5710766c6e293334d9560adf31b863f3 internalID: -3905254919024616460 vertices: [] indices: @@ -289,7 +289,7 @@ TextureImporter: physicsShape: [] tessellationDetail: 0 bones: [] - spriteID: 9c69e10b505202e4090ea4cf12d1086a + spriteID: 55b43fd20e653694393404ce1749357a internalID: -4552252610629298190 vertices: [] indices: From e6c3148764addb4f717d1adb106ee72a44290f87 Mon Sep 17 00:00:00 2001 From: Ding Yuntian <1491671119@qq.com> Date: Sun, 2 Aug 2026 19:49:44 +0800 Subject: [PATCH 2/4] =?UTF-8?q?fix:=20=E6=A2=A6=E5=A2=83=E8=A1=8C=E5=8A=A0?= =?UTF-8?q?=E9=95=BF?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Assets/Prefabs/Bubbles/OnelineBox.prefab | 16 ++++++------- Assets/Scenes/Persistence.unity | 30 +++--------------------- 2 files changed, 11 insertions(+), 35 deletions(-) diff --git a/Assets/Prefabs/Bubbles/OnelineBox.prefab b/Assets/Prefabs/Bubbles/OnelineBox.prefab index f3ee8e2ea..66f941c77 100644 --- a/Assets/Prefabs/Bubbles/OnelineBox.prefab +++ b/Assets/Prefabs/Bubbles/OnelineBox.prefab @@ -35,10 +35,10 @@ RectTransform: m_Children: [] m_Father: {fileID: 1284103664181101717} m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} - m_AnchorMin: {x: 0, y: 0} - m_AnchorMax: {x: 0, y: 0} - m_AnchoredPosition: {x: 0, y: 0} - m_SizeDelta: {x: 0, y: 0} + m_AnchorMin: {x: 0, y: 1} + m_AnchorMax: {x: 0, y: 1} + m_AnchoredPosition: {x: 500, y: -15.116279} + m_SizeDelta: {x: 1000, y: 0} m_Pivot: {x: 0.5, y: 0} --- !u!222 &7304535519653304073 CanvasRenderer: @@ -205,8 +205,8 @@ MonoBehaviour: m_PersistentCalls: m_Calls: [] waitForNormalChars: 0.03 - waitLong: 0.6 - waitMiddle: 0.2 + waitLong: 0.03 + waitMiddle: 0.03 avoidMultiplePunctuationWait: 0 waitForNewLines: 1 waitForLastCharacter: 1 @@ -228,7 +228,7 @@ MonoBehaviour: m_IgnoreLayout: 0 m_MinWidth: 10 m_MinHeight: -1 - m_PreferredWidth: 800 + m_PreferredWidth: 1000 m_PreferredHeight: -1 m_FlexibleWidth: -1 m_FlexibleHeight: -1 @@ -252,7 +252,7 @@ GameObject: m_Icon: {fileID: 0} m_NavMeshLayer: 0 m_StaticEditorFlags: 0 - m_IsActive: 1 + m_IsActive: 0 --- !u!224 &1284103664181101717 RectTransform: m_ObjectHideFlags: 0 diff --git a/Assets/Scenes/Persistence.unity b/Assets/Scenes/Persistence.unity index f849275ec..221b7cbec 100644 --- a/Assets/Scenes/Persistence.unity +++ b/Assets/Scenes/Persistence.unity @@ -3674,7 +3674,7 @@ MonoBehaviour: m_EditorClassIdentifier: content: {fileID: 2090948093} box: {fileID: 2090948094} - maxWidth: 800 + maxWidth: 1000 minWidth: 10 --- !u!1 &1039398627 GameObject: @@ -8109,11 +8109,11 @@ PrefabInstance: objectReference: {fileID: 0} - target: {fileID: 1284103664181101717, guid: 60f4427b2070be04f90c1486bb86062c, type: 3} propertyPath: m_SizeDelta.x - value: 0 + value: 1000 objectReference: {fileID: 0} - target: {fileID: 1284103664181101717, guid: 60f4427b2070be04f90c1486bb86062c, type: 3} propertyPath: m_SizeDelta.y - value: 0 + value: 30.232557 objectReference: {fileID: 0} - target: {fileID: 1284103664181101717, guid: 60f4427b2070be04f90c1486bb86062c, type: 3} propertyPath: m_LocalPosition.x @@ -8163,34 +8163,10 @@ PrefabInstance: propertyPath: m_LocalEulerAnglesHint.z value: 0 objectReference: {fileID: 0} - - target: {fileID: 5463321828217487701, guid: 60f4427b2070be04f90c1486bb86062c, type: 3} - propertyPath: m_AnchorMax.y - value: 0 - objectReference: {fileID: 0} - - target: {fileID: 5463321828217487701, guid: 60f4427b2070be04f90c1486bb86062c, type: 3} - propertyPath: m_AnchorMin.y - value: 0 - objectReference: {fileID: 0} - - target: {fileID: 5463321828217487701, guid: 60f4427b2070be04f90c1486bb86062c, type: 3} - propertyPath: m_SizeDelta.x - value: 0 - objectReference: {fileID: 0} - - target: {fileID: 5463321828217487701, guid: 60f4427b2070be04f90c1486bb86062c, type: 3} - propertyPath: m_AnchoredPosition.x - value: 0 - objectReference: {fileID: 0} - - target: {fileID: 5463321828217487701, guid: 60f4427b2070be04f90c1486bb86062c, type: 3} - propertyPath: m_AnchoredPosition.y - value: 0 - objectReference: {fileID: 0} - target: {fileID: 8352794458094198668, guid: 60f4427b2070be04f90c1486bb86062c, type: 3} propertyPath: m_Name value: OnelineBox objectReference: {fileID: 0} - - target: {fileID: 8352794458094198668, guid: 60f4427b2070be04f90c1486bb86062c, type: 3} - propertyPath: m_IsActive - value: 0 - objectReference: {fileID: 0} m_RemovedComponents: [] m_RemovedGameObjects: [] m_AddedGameObjects: [] From 93501dddaf07259b63d10ec8204f87219f3eb280 Mon Sep 17 00:00:00 2001 From: Ding Yuntian <1491671119@qq.com> Date: Sun, 2 Aug 2026 21:20:05 +0800 Subject: [PATCH 3/4] =?UTF-8?q?feat:=20=E5=A2=9E=E5=8A=A0=E6=9C=AC?= =?UTF-8?q?=E5=9C=B0=E5=8C=96=E5=9B=BE=E7=89=87=E6=92=AD=E6=94=BE?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Assets/Editor/Localization.meta | 8 + .../LocalizedObjSpriteValidator.cs | 168 ++++++++++++++++++ .../LocalizedObjSpriteValidator.cs.meta | 11 ++ .../Editor/Tests/LocalizedObjSpriteTests.cs | 97 ++++++++++ .../Tests/LocalizedObjSpriteTests.cs.meta | 11 ++ .../Editor/Tests/SaveSnapshotContractTests.cs | 14 +- .../Prefabs/TerminalChapterPanel.prefab | 2 +- .../Prefabs/TerminalSettingPanelMini.prefab | 8 +- .../Prefabs/TerminalStartPanel.prefab | 2 +- Assets/Scenes/Persistence.unity | 12 -- .../Scripts/Dialog System/BaseYarnCommand.cs | 14 ++ Assets/Scripts/SaveSystem/SaveSnapshot.cs | 2 + .../PlayTool/LocalizedObjSpriteNaming.cs | 100 +++++++++++ .../PlayTool/LocalizedObjSpriteNaming.cs.meta | 11 ++ Assets/Scripts/UI/Panel/PlayToolPanel.cs | 125 ++++++++++++- 15 files changed, 557 insertions(+), 28 deletions(-) create mode 100644 Assets/Editor/Localization.meta create mode 100644 Assets/Editor/Localization/LocalizedObjSpriteValidator.cs create mode 100644 Assets/Editor/Localization/LocalizedObjSpriteValidator.cs.meta create mode 100644 Assets/Editor/Tests/LocalizedObjSpriteTests.cs create mode 100644 Assets/Editor/Tests/LocalizedObjSpriteTests.cs.meta create mode 100644 Assets/Scripts/UI/Panel/PlayTool/LocalizedObjSpriteNaming.cs create mode 100644 Assets/Scripts/UI/Panel/PlayTool/LocalizedObjSpriteNaming.cs.meta diff --git a/Assets/Editor/Localization.meta b/Assets/Editor/Localization.meta new file mode 100644 index 000000000..6f91e2d37 --- /dev/null +++ b/Assets/Editor/Localization.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: d3a91a90ebc2fd742bd10a66893940b8 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Editor/Localization/LocalizedObjSpriteValidator.cs b/Assets/Editor/Localization/LocalizedObjSpriteValidator.cs new file mode 100644 index 000000000..1bdc75c65 --- /dev/null +++ b/Assets/Editor/Localization/LocalizedObjSpriteValidator.cs @@ -0,0 +1,168 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using AibisDream.UI; +using UnityEditor; +using UnityEditor.Build; +using UnityEditor.Build.Reporting; +using UnityEngine; + +namespace AibisDream.EditorTools.Localization +{ + public readonly struct LocalizedObjSpriteValidationIssue + { + public LocalizedObjSpriteValidationIssue(string assetPath, string message) + { + AssetPath = assetPath; + Message = message; + } + + public string AssetPath { get; } + public string Message { get; } + + public override string ToString() + { + return $"{AssetPath}: {Message}"; + } + } + + /// + /// Validates localized Obj sprite groups without depending on Yarn references or + /// Unity Localization asset tables. A group becomes localized as soon as one + /// supported non-Chinese suffix is present. + /// + public static class LocalizedObjSpriteValidator + { + internal const string DefaultObjSpriteRoot = "Assets/RawResources/Art/Obj"; + private const string MenuPath = + "Tools/AIBIS/Localization/Validate Localized Obj Sprites"; + + [MenuItem(MenuPath)] + public static void ValidateFromMenu() + { + IReadOnlyList issues = Validate(); + LogIssues(issues); + if (issues.Count == 0) + { + Debug.Log( + "[LocalizedObjSpriteValidator] Validation passed. " + + "All detected localized Obj sprite groups are complete."); + } + } + + public static IReadOnlyList Validate( + string rootPath = DefaultObjSpriteRoot) + { + var issues = new List(); + if (!Directory.Exists(rootPath)) + { + issues.Add(new LocalizedObjSpriteValidationIssue( + ToAssetPath(rootPath), + "Obj sprite root does not exist.")); + return issues; + } + + var localizedGroups = new HashSet(StringComparer.OrdinalIgnoreCase); + foreach (string filePath in Directory.EnumerateFiles( + rootPath, + "*.png", + SearchOption.AllDirectories)) + { + string fileName = Path.GetFileNameWithoutExtension(filePath); + if (!LocalizedObjSpriteNaming.TryParseLocalizedVariant( + fileName, + out string logicalName, + out _)) + { + continue; + } + + if (string.IsNullOrWhiteSpace(logicalName)) + { + issues.Add(new LocalizedObjSpriteValidationIssue( + ToAssetPath(filePath), + "Localized variant has an empty logical name.")); + continue; + } + + string directory = Path.GetDirectoryName(filePath) ?? rootPath; + localizedGroups.Add(Path.Combine(directory, logicalName)); + } + + foreach (string groupPath in localizedGroups.OrderBy( + path => path, + StringComparer.OrdinalIgnoreCase)) + { + ValidateRequiredFile( + $"{groupPath}.png", + LocalizedObjSpriteNaming.DefaultLocaleCode, + issues); + + foreach (string localeCode in + LocalizedObjSpriteNaming.NonDefaultLocaleCodes) + { + ValidateRequiredFile( + $"{groupPath}-{localeCode}.png", + localeCode, + issues); + } + } + + return issues; + } + + internal static void LogIssues( + IReadOnlyList issues) + { + foreach (LocalizedObjSpriteValidationIssue issue in issues) + Debug.LogError($"[LocalizedObjSpriteValidator] {issue}"); + } + + private static void ValidateRequiredFile( + string filePath, + string localeCode, + ICollection issues) + { + if (File.Exists(filePath)) + return; + + issues.Add(new LocalizedObjSpriteValidationIssue( + ToAssetPath(filePath), + $"Missing required '{localeCode}' sprite variant.")); + } + + private static string ToAssetPath(string path) + { + return path.Replace('\\', '/'); + } + } + + public sealed class LocalizedObjSpriteBuildValidator : IPreprocessBuildWithReport + { + // Run before build preprocessors that mutate version information. + public int callbackOrder => -1000; + + public void OnPreprocessBuild(BuildReport report) + { + IReadOnlyList issues = + LocalizedObjSpriteValidator.Validate(); + if (issues.Count == 0) + return; + + LocalizedObjSpriteValidator.LogIssues(issues); + string summary = string.Join( + Environment.NewLine, + issues.Take(20).Select(issue => issue.ToString())); + if (issues.Count > 20) + { + summary += Environment.NewLine + + $"... and {issues.Count - 20} more issue(s)."; + } + + throw new BuildFailedException( + "Localized Obj sprite validation failed:" + + Environment.NewLine + summary); + } + } +} diff --git a/Assets/Editor/Localization/LocalizedObjSpriteValidator.cs.meta b/Assets/Editor/Localization/LocalizedObjSpriteValidator.cs.meta new file mode 100644 index 000000000..17fcfcaba --- /dev/null +++ b/Assets/Editor/Localization/LocalizedObjSpriteValidator.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 686ca5acbb6647d4c802ecd2a8ba043d +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Editor/Tests/LocalizedObjSpriteTests.cs b/Assets/Editor/Tests/LocalizedObjSpriteTests.cs new file mode 100644 index 000000000..521dbbfe2 --- /dev/null +++ b/Assets/Editor/Tests/LocalizedObjSpriteTests.cs @@ -0,0 +1,97 @@ +using System; +using System.IO; +using System.Linq; +using AibisDream.EditorTools.Localization; +using AibisDream.UI; +using NUnit.Framework; + +namespace AibisDream.SystemEditor.Tests +{ + public sealed class LocalizedObjSpriteTests + { + private string temporaryRoot; + + [SetUp] + public void SetUp() + { + temporaryRoot = Path.Combine( + Path.GetTempPath(), + "AibisDreamLocalizedObjSpriteTests", + Guid.NewGuid().ToString("N")); + Directory.CreateDirectory(temporaryRoot); + } + + [TearDown] + public void TearDown() + { + if (!string.IsNullOrEmpty(temporaryRoot) && Directory.Exists(temporaryRoot)) + Directory.Delete(temporaryRoot, true); + } + + [TestCase("zh-Hans", "病历")] + [TestCase("zh-CN", "病历")] + [TestCase("en", "病历-en")] + [TestCase("en-US", "病历-en")] + [TestCase("ja-JP", "病历-ja-JP")] + [TestCase("ru-RU", "病历-ru")] + [TestCase("es-ES", "病历-es")] + [TestCase("pt-BR", "病历-pt-BR")] + public void GetPicName_UsesCanonicalLocaleSuffix( + string localeCode, + string expected) + { + Assert.That( + LocalizedObjSpriteNaming.GetPicName("病历.png", localeCode), + Is.EqualTo(expected)); + } + + [Test] + public void CandidateNames_FallBackToUnsuffixedChineseWithoutDuplicates() + { + Assert.That( + LocalizedObjSpriteNaming.GetCandidatePicNames("病历.png", "en"), + Is.EqualTo(new[] { "病历-en", "病历" })); + Assert.That( + LocalizedObjSpriteNaming.GetCandidatePicNames("病历.png", "zh-Hans"), + Is.EqualTo(new[] { "病历" })); + } + + [Test] + public void Validator_IgnoresUnsuffixedOnlySprites() + { + WriteSprite("普通图片.png"); + + Assert.That( + LocalizedObjSpriteValidator.Validate(temporaryRoot), + Is.Empty); + } + + [Test] + public void Validator_RequiresEveryVariantAfterAGroupIsDetected() + { + WriteCompleteGroup("病历"); + Assert.That( + LocalizedObjSpriteValidator.Validate(temporaryRoot), + Is.Empty); + + File.Delete(Path.Combine(temporaryRoot, "病历-ru.png")); + var issues = LocalizedObjSpriteValidator.Validate(temporaryRoot); + + Assert.That(issues, Has.Count.EqualTo(1)); + Assert.That(issues.Single().AssetPath, Does.EndWith("病历-ru.png")); + Assert.That(issues.Single().Message, Does.Contain("'ru'")); + } + + private void WriteCompleteGroup(string logicalName) + { + WriteSprite($"{logicalName}.png"); + foreach (string localeCode in LocalizedObjSpriteNaming.NonDefaultLocaleCodes) + WriteSprite($"{logicalName}-{localeCode}.png"); + } + + private void WriteSprite(string fileName) + { + File.WriteAllBytes(Path.Combine(temporaryRoot, fileName), Array.Empty()); + } + } +} diff --git a/Assets/Editor/Tests/LocalizedObjSpriteTests.cs.meta b/Assets/Editor/Tests/LocalizedObjSpriteTests.cs.meta new file mode 100644 index 000000000..2f13e56a9 --- /dev/null +++ b/Assets/Editor/Tests/LocalizedObjSpriteTests.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 212d27ffec81bcb48a886db924f43e1a +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Editor/Tests/SaveSnapshotContractTests.cs b/Assets/Editor/Tests/SaveSnapshotContractTests.cs index e3b7b87a7..99473a8c8 100644 --- a/Assets/Editor/Tests/SaveSnapshotContractTests.cs +++ b/Assets/Editor/Tests/SaveSnapshotContractTests.cs @@ -36,7 +36,11 @@ namespace AibisDream.SystemEditor.Tests source.sections[SnapshotProviderIds.PlayTool] = new PlayToolSnapshotDto { isObjVisible = true, - objPicName = "证物" + objPicName = "证物", + objIsLocalized = true, + isFullScreenVisible = true, + fullScreenPicName = "病历", + fullScreenIsLocalized = true }; var restored = JsonConvert.DeserializeObject( @@ -50,6 +54,14 @@ namespace AibisDream.SystemEditor.Tests Assert.That(showcase.displayMode, Is.EqualTo("Large")); Assert.That(showcase.picName, Is.EqualTo("D2S星图")); Assert.That(playTool.objPicName, Is.EqualTo("证物")); + Assert.That(playTool.objIsLocalized, Is.True); + Assert.That(playTool.fullScreenPicName, Is.EqualTo("病历")); + Assert.That(playTool.fullScreenIsLocalized, Is.True); + + var legacyPlayTool = JsonConvert.DeserializeObject( + "{\"isObjVisible\":true,\"objPicName\":\"legacy\"}"); + Assert.That(legacyPlayTool.objIsLocalized, Is.False); + Assert.That(legacyPlayTool.fullScreenIsLocalized, Is.False); var legacy = JsonConvert.DeserializeObject( "{\"schemaVersion\":1,\"sections\":{}}"); diff --git a/Assets/GameContent/Feature_MainUI/Prefabs/TerminalChapterPanel.prefab b/Assets/GameContent/Feature_MainUI/Prefabs/TerminalChapterPanel.prefab index 3a5b2aec0..e04d1eae1 100644 --- a/Assets/GameContent/Feature_MainUI/Prefabs/TerminalChapterPanel.prefab +++ b/Assets/GameContent/Feature_MainUI/Prefabs/TerminalChapterPanel.prefab @@ -692,7 +692,7 @@ MonoBehaviour: m_EditorClassIdentifier: canvasGroup: {fileID: 4638119140970039337} sweep: {fileID: 0} - visibleAlpha: 0.6 + visibleAlpha: 0.8 fadeDuration: 0.06 showEase: 1 hideEase: 1 diff --git a/Assets/GameContent/Feature_MainUI/Prefabs/TerminalSettingPanelMini.prefab b/Assets/GameContent/Feature_MainUI/Prefabs/TerminalSettingPanelMini.prefab index e15662a1f..f933e317d 100644 --- a/Assets/GameContent/Feature_MainUI/Prefabs/TerminalSettingPanelMini.prefab +++ b/Assets/GameContent/Feature_MainUI/Prefabs/TerminalSettingPanelMini.prefab @@ -67,7 +67,7 @@ MonoBehaviour: m_EditorClassIdentifier: canvasGroup: {fileID: 2695752103100139348} sweep: {fileID: 0} - visibleAlpha: 0.6 + visibleAlpha: 0.8 fadeDuration: 0.06 showEase: 1 hideEase: 1 @@ -385,7 +385,7 @@ PrefabInstance: objectReference: {fileID: 0} - target: {fileID: 27177156846382951, guid: e45f5fd66e7512444a9a27161cf51b7f, type: 3} propertyPath: m_SizeDelta.x - value: 240 + value: 350 objectReference: {fileID: 0} - target: {fileID: 27177156846382951, guid: e45f5fd66e7512444a9a27161cf51b7f, type: 3} propertyPath: m_AnchoredPosition.x @@ -650,7 +650,7 @@ PrefabInstance: objectReference: {fileID: 0} - target: {fileID: 27177156846382951, guid: e45f5fd66e7512444a9a27161cf51b7f, type: 3} propertyPath: m_SizeDelta.x - value: 240 + value: 350 objectReference: {fileID: 0} - target: {fileID: 27177156846382951, guid: e45f5fd66e7512444a9a27161cf51b7f, type: 3} propertyPath: m_AnchoredPosition.x @@ -915,7 +915,7 @@ PrefabInstance: objectReference: {fileID: 0} - target: {fileID: 27177156846382951, guid: e45f5fd66e7512444a9a27161cf51b7f, type: 3} propertyPath: m_SizeDelta.x - value: 240 + value: 350 objectReference: {fileID: 0} - target: {fileID: 27177156846382951, guid: e45f5fd66e7512444a9a27161cf51b7f, type: 3} propertyPath: m_AnchoredPosition.x diff --git a/Assets/GameContent/Feature_MainUI/Prefabs/TerminalStartPanel.prefab b/Assets/GameContent/Feature_MainUI/Prefabs/TerminalStartPanel.prefab index 144f8fbb8..16045e048 100644 --- a/Assets/GameContent/Feature_MainUI/Prefabs/TerminalStartPanel.prefab +++ b/Assets/GameContent/Feature_MainUI/Prefabs/TerminalStartPanel.prefab @@ -66,7 +66,7 @@ MonoBehaviour: m_EditorClassIdentifier: canvasGroup: {fileID: 5054907813288028710} sweep: {fileID: 0} - visibleAlpha: 0.6 + visibleAlpha: 0.8 fadeDuration: 0.06 showEase: 1 hideEase: 1 diff --git a/Assets/Scenes/Persistence.unity b/Assets/Scenes/Persistence.unity index 221b7cbec..0aa155a11 100644 --- a/Assets/Scenes/Persistence.unity +++ b/Assets/Scenes/Persistence.unity @@ -8704,10 +8704,6 @@ PrefabInstance: serializedVersion: 3 m_TransformParent: {fileID: 834154072} m_Modifications: - - target: {fileID: 36740575257710488, guid: 9514dab519fa1f24dacc2903b8f331c3, type: 3} - propertyPath: m_SizeDelta.x - value: 350 - objectReference: {fileID: 0} - target: {fileID: 270038765062520215, guid: 9514dab519fa1f24dacc2903b8f331c3, type: 3} propertyPath: m_Pivot.x value: 0.5 @@ -8788,14 +8784,6 @@ PrefabInstance: propertyPath: m_LocalEulerAnglesHint.z value: 0 objectReference: {fileID: 0} - - target: {fileID: 497019700501100578, guid: 9514dab519fa1f24dacc2903b8f331c3, type: 3} - propertyPath: m_SizeDelta.x - value: 350 - objectReference: {fileID: 0} - - target: {fileID: 827752375999759650, guid: 9514dab519fa1f24dacc2903b8f331c3, type: 3} - propertyPath: m_SizeDelta.x - value: 350 - objectReference: {fileID: 0} - target: {fileID: 1011642959047981851, guid: 9514dab519fa1f24dacc2903b8f331c3, type: 3} propertyPath: m_Name value: TerminalSettingPanel diff --git a/Assets/Scripts/Dialog System/BaseYarnCommand.cs b/Assets/Scripts/Dialog System/BaseYarnCommand.cs index cc29f9ed4..5ca1c728a 100644 --- a/Assets/Scripts/Dialog System/BaseYarnCommand.cs +++ b/Assets/Scripts/Dialog System/BaseYarnCommand.cs @@ -402,6 +402,13 @@ namespace AibisDream return panel.ShowObj(picName, duration); } + [YarnCommand("show_l10n_obj")] + public static IEnumerator ShowL10nObj(string picName, float duration = 1) + { + var panel = UIManager.Instance.GetPanel(); + return panel.ShowL10nObj(picName, duration); + } + [YarnCommand("hide_obj")] public static IEnumerator HideObj(float duration = 1) { @@ -470,6 +477,13 @@ namespace AibisDream return panel.ShowFullScreen(picName, duration); } + [YarnCommand("show_l10n_full_screen")] + public static IEnumerator ShowL10nFullScreen(string picName, float duration = 1) + { + var panel = UIManager.Instance.GetPanel(); + return panel.ShowL10nFullScreen(picName, duration); + } + [YarnCommand("hide_full_screen")] public static IEnumerator HideFullScreen(float duration = 1) { diff --git a/Assets/Scripts/SaveSystem/SaveSnapshot.cs b/Assets/Scripts/SaveSystem/SaveSnapshot.cs index a9961d27f..f2ba024da 100644 --- a/Assets/Scripts/SaveSystem/SaveSnapshot.cs +++ b/Assets/Scripts/SaveSystem/SaveSnapshot.cs @@ -197,8 +197,10 @@ namespace AibisDream.SaveSystem { public bool isObjVisible; public string objPicName; + public bool objIsLocalized; public bool isFullScreenVisible; public string fullScreenPicName; + public bool fullScreenIsLocalized; } public enum ShowcaseDisplayMode diff --git a/Assets/Scripts/UI/Panel/PlayTool/LocalizedObjSpriteNaming.cs b/Assets/Scripts/UI/Panel/PlayTool/LocalizedObjSpriteNaming.cs new file mode 100644 index 000000000..fff0e1486 --- /dev/null +++ b/Assets/Scripts/UI/Panel/PlayTool/LocalizedObjSpriteNaming.cs @@ -0,0 +1,100 @@ +using System; +using System.Collections.Generic; +using AibisDream.Framework; + +namespace AibisDream.UI +{ + /// + /// Defines the file-name convention used by localized Obj sprites. + /// Chinese uses the unsuffixed source asset; every other supported locale uses + /// "{logicalName}-{localeCode}". + /// + internal static class LocalizedObjSpriteNaming + { + internal const string DefaultLocaleCode = "zh-Hans"; + + private static readonly string[] NonDefaultLocaleCodeValues = + { + "en", + "ja-JP", + "ru", + "es", + "pt-BR" + }; + + internal static IReadOnlyList NonDefaultLocaleCodes => + NonDefaultLocaleCodeValues; + + internal static string NormalizeLogicalName(string picName) + { + if (string.IsNullOrEmpty(picName)) return picName; + return picName.EndsWith(".png", StringComparison.OrdinalIgnoreCase) + ? picName.Substring(0, picName.Length - 4) + : picName; + } + + internal static string GetAssetLocaleCode(string localeCode) + { + return LocalizationKit.GetLocaleKind(localeCode) switch + { + LocaleKind.En => "en", + LocaleKind.Ja => "ja-JP", + LocaleKind.Ru => "ru", + LocaleKind.Es => "es", + LocaleKind.PtBr => "pt-BR", + _ => DefaultLocaleCode + }; + } + + internal static string GetPicName(string logicalName, string localeCode) + { + string normalizedName = NormalizeLogicalName(logicalName); + string assetLocaleCode = GetAssetLocaleCode(localeCode); + return string.Equals( + assetLocaleCode, + DefaultLocaleCode, + StringComparison.Ordinal) + ? normalizedName + : $"{normalizedName}-{assetLocaleCode}"; + } + + internal static IReadOnlyList GetCandidatePicNames( + string logicalName, + string localeCode) + { + string normalizedName = NormalizeLogicalName(logicalName); + string localizedName = GetPicName(normalizedName, localeCode); + if (string.Equals(localizedName, normalizedName, StringComparison.Ordinal)) + return new[] { normalizedName }; + + return new[] { localizedName, normalizedName }; + } + + internal static bool TryParseLocalizedVariant( + string fileNameWithoutExtension, + out string logicalName, + out string localeCode) + { + foreach (string candidateCode in NonDefaultLocaleCodeValues) + { + string suffix = $"-{candidateCode}"; + if (!fileNameWithoutExtension.EndsWith( + suffix, + StringComparison.OrdinalIgnoreCase)) + { + continue; + } + + logicalName = fileNameWithoutExtension.Substring( + 0, + fileNameWithoutExtension.Length - suffix.Length); + localeCode = candidateCode; + return true; + } + + logicalName = null; + localeCode = null; + return false; + } + } +} diff --git a/Assets/Scripts/UI/Panel/PlayTool/LocalizedObjSpriteNaming.cs.meta b/Assets/Scripts/UI/Panel/PlayTool/LocalizedObjSpriteNaming.cs.meta new file mode 100644 index 000000000..b8cf38f51 --- /dev/null +++ b/Assets/Scripts/UI/Panel/PlayTool/LocalizedObjSpriteNaming.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 81844af51d08a8e40adc1a25247f2337 +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 f4792e90c..5e6d83cc6 100644 --- a/Assets/Scripts/UI/Panel/PlayToolPanel.cs +++ b/Assets/Scripts/UI/Panel/PlayToolPanel.cs @@ -1,5 +1,6 @@ using System; using System.Collections; +using System.Collections.Generic; using AibisDream.Framework; using AibisDream.Kit; using AibisDream.SaveSystem; @@ -7,6 +8,7 @@ using AibisDream.Utility; using DG.Tweening; using RainbowArt.CleanFlatUI; using UnityEngine; +using UnityEngine.Localization.Settings; using UnityEngine.ResourceManagement.AsyncOperations; using UnityEngine.UI; @@ -41,6 +43,8 @@ namespace AibisDream.UI private int _fullScreenOperationVersion; private string _currentObjPicName; private string _currentFullScreenPicName; + private bool _currentObjIsLocalized; + private bool _currentFullScreenIsLocalized; private bool _isObjVisible; private bool _isFullScreenVisible; @@ -64,10 +68,7 @@ namespace AibisDream.UI private static string NormalizeObjPicName(string picName) { - if (string.IsNullOrEmpty(picName)) return picName; - return picName.EndsWith(".png", StringComparison.OrdinalIgnoreCase) - ? picName.Substring(0, picName.Length - 4) - : picName; + return LocalizedObjSpriteNaming.NormalizeLogicalName(picName); } private IEnumerator LoadObjSprite( @@ -91,6 +92,62 @@ namespace AibisDream.UI completed?.Invoke(null); } + private IEnumerator LoadLocalizedObjSprite( + string logicalName, + Action completed, + Action warning = null) + { + yield return LocalizationKit.WaitUntilReady(); + + string localeCode = LocalizationSettings.SelectedLocale?.Identifier.Code; + IReadOnlyList candidates = + LocalizedObjSpriteNaming.GetCandidatePicNames(logicalName, localeCode); + + for (int index = 0; index < candidates.Count; index++) + { + Sprite sprite = null; + Action loadWarning = index == candidates.Count - 1 + ? warning + : null; + yield return LoadObjSprite( + candidates[index], + value => sprite = value, + loadWarning); + if (sprite == null) + continue; + + if (index > 0) + { + string message = + $"[PlayToolPanel] Localized sprite is missing for locale " + + $"'{LocalizedObjSpriteNaming.GetAssetLocaleCode(localeCode)}': " + + $"{NormalizeObjPicName(logicalName)}. Falling back to zh-Hans."; + Debug.LogWarning(message); + warning?.Invoke(message); + } + + completed?.Invoke(sprite); + yield break; + } + + completed?.Invoke(null); + } + + private IEnumerator LoadObjSprite( + string picName, + bool isLocalized, + Action completed, + Action warning = null) + { + if (isLocalized) + { + yield return LoadLocalizedObjSprite(picName, completed, warning); + yield break; + } + + yield return LoadObjSprite(picName, completed, warning); + } + private void ApplyObjSprite(Sprite sprite) { var imageAspect = (float)sprite.texture.width / sprite.texture.height; @@ -113,6 +170,7 @@ namespace AibisDream.UI _showObjImage.SetAlpha(0f); _showObjPanel.SetActive(false); _currentObjPicName = null; + _currentObjIsLocalized = false; _isObjVisible = false; } @@ -123,6 +181,7 @@ namespace AibisDream.UI _fullScreenImage.SetAlpha(0f); _fullScreenImage.gameObject.SetActive(false); _currentFullScreenPicName = null; + _currentFullScreenIsLocalized = false; _isFullScreenVisible = false; } @@ -131,12 +190,28 @@ namespace AibisDream.UI /// /// 协程 public IEnumerator ShowObj(string picName, float duration = 1) + { + yield return ShowObjInternal(picName, duration, false); + } + + public IEnumerator ShowL10nObj(string picName, float duration = 1) + { + yield return ShowObjInternal(picName, duration, true); + } + + private IEnumerator ShowObjInternal( + string picName, + float duration, + bool isLocalized) { var version = ++_objOperationVersion; _showObjImage.DOKill(); Sprite newSprite = null; - yield return LoadObjSprite(picName, value => newSprite = value); + yield return LoadObjSprite( + picName, + isLocalized, + value => newSprite = value); if (version != _objOperationVersion) { yield break; @@ -155,6 +230,7 @@ namespace AibisDream.UI if (version != _objOperationVersion) yield break; _currentObjPicName = NormalizeObjPicName(picName); + _currentObjIsLocalized = isLocalized; _isObjVisible = true; } @@ -162,6 +238,7 @@ namespace AibisDream.UI { var version = ++_objOperationVersion; _currentObjPicName = null; + _currentObjIsLocalized = false; _isObjVisible = false; _showObjImage.DOKill(); yield return _showObjImage.FadeOutAsync(duration); @@ -170,13 +247,29 @@ namespace AibisDream.UI } public IEnumerator ShowFullScreen(string picName, float duration = 1) + { + yield return ShowFullScreenInternal(picName, duration, false); + } + + public IEnumerator ShowL10nFullScreen(string picName, float duration = 1) + { + yield return ShowFullScreenInternal(picName, duration, true); + } + + private IEnumerator ShowFullScreenInternal( + string picName, + float duration, + bool isLocalized) { var version = ++_fullScreenOperationVersion; _fullScreenImage.DOKill(); _fullScreenImage.gameObject.SetActive(true); Sprite newSprite = null; - yield return LoadObjSprite(picName, value => newSprite = value); + yield return LoadObjSprite( + picName, + isLocalized, + value => newSprite = value); if (version != _fullScreenOperationVersion) { yield break; @@ -194,6 +287,7 @@ namespace AibisDream.UI if (version != _fullScreenOperationVersion) yield break; _currentFullScreenPicName = NormalizeObjPicName(picName); + _currentFullScreenIsLocalized = isLocalized; _isFullScreenVisible = true; } @@ -201,6 +295,7 @@ namespace AibisDream.UI { var version = ++_fullScreenOperationVersion; _currentFullScreenPicName = null; + _currentFullScreenIsLocalized = false; _isFullScreenVisible = false; _fullScreenImage.DOKill(); yield return _fullScreenImage.FadeOutAsync(duration); @@ -214,8 +309,10 @@ namespace AibisDream.UI { isObjVisible = _isObjVisible, objPicName = _currentObjPicName, + objIsLocalized = _currentObjIsLocalized, isFullScreenVisible = _isFullScreenVisible, - fullScreenPicName = _currentFullScreenPicName + fullScreenPicName = _currentFullScreenPicName, + fullScreenIsLocalized = _currentFullScreenIsLocalized }; } @@ -229,13 +326,18 @@ namespace AibisDream.UI { var version = ++_objOperationVersion; Sprite sprite = null; - yield return LoadObjSprite(snapshot.objPicName, value => sprite = value, warning); + yield return LoadObjSprite( + snapshot.objPicName, + snapshot.objIsLocalized, + value => sprite = value, + warning); if (version == _objOperationVersion && sprite != null) { ApplyObjSprite(sprite); _showObjPanel.SetActive(true); _showObjImage.SetAlpha(1f); _currentObjPicName = NormalizeObjPicName(snapshot.objPicName); + _currentObjIsLocalized = snapshot.objIsLocalized; _isObjVisible = true; } } @@ -248,13 +350,18 @@ namespace AibisDream.UI { var version = ++_fullScreenOperationVersion; Sprite sprite = null; - yield return LoadObjSprite(snapshot.fullScreenPicName, value => sprite = value, warning); + yield return LoadObjSprite( + snapshot.fullScreenPicName, + snapshot.fullScreenIsLocalized, + value => sprite = value, + warning); if (version == _fullScreenOperationVersion && sprite != null) { _fullScreenImage.sprite = sprite; _fullScreenImage.gameObject.SetActive(true); _fullScreenImage.SetAlpha(1f); _currentFullScreenPicName = NormalizeObjPicName(snapshot.fullScreenPicName); + _currentFullScreenIsLocalized = snapshot.fullScreenIsLocalized; _isFullScreenVisible = true; } } From 5fa324de6c2a7cbc7831ec9fdc30138c386d9632 Mon Sep 17 00:00:00 2001 From: Ding Yuntian <1491671119@qq.com> Date: Sun, 2 Aug 2026 21:45:36 +0800 Subject: [PATCH 4/4] =?UTF-8?q?fix:=20=E5=88=87=E5=89=B2=E9=94=AF=E5=AD=90?= =?UTF-8?q?=E6=8E=A5=E5=85=A5EventTrigger?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Assets/Prefabs/Cut/CutWheel.prefab | 58 +++++++++++ Assets/Prefabs/Cut/托盘 (1).prefab | 45 +-------- .../FixSystemNew/BodyModule/CuttingManager.cs | 32 +++---- .../FixSystemNew/BodyModule/GearFollower.cs | 96 ++++++++++++++++++- 4 files changed, 167 insertions(+), 64 deletions(-) diff --git a/Assets/Prefabs/Cut/CutWheel.prefab b/Assets/Prefabs/Cut/CutWheel.prefab index 42fe5afe3..f7b728735 100644 --- a/Assets/Prefabs/Cut/CutWheel.prefab +++ b/Assets/Prefabs/Cut/CutWheel.prefab @@ -338,6 +338,8 @@ GameObject: - component: {fileID: 3356305640573621166} - component: {fileID: 593249056707730865} - component: {fileID: 699543252252589786} + - component: {fileID: 780000000000000001} + - component: {fileID: 780000000000000002} m_Layer: 0 m_Name: CutWheel m_TagString: Untagged @@ -415,6 +417,62 @@ SpriteRenderer: m_WasSpriteAssigned: 1 m_MaskInteraction: 0 m_SpriteSortPoint: 0 +--- !u!58 &780000000000000001 +CircleCollider2D: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 6388638343240283516} + m_Enabled: 1 + m_Density: 1 + m_Material: {fileID: 0} + m_IncludeLayers: + serializedVersion: 2 + m_Bits: 0 + m_ExcludeLayers: + serializedVersion: 2 + m_Bits: 0 + m_LayerOverridePriority: 0 + m_ForceSendLayers: + serializedVersion: 2 + m_Bits: 4294967295 + m_ForceReceiveLayers: + serializedVersion: 2 + m_Bits: 4294967295 + m_ContactCaptureLayers: + serializedVersion: 2 + m_Bits: 4294967295 + m_CallbackLayers: + serializedVersion: 2 + m_Bits: 4294967295 + m_IsTrigger: 1 + m_UsedByEffector: 0 + m_UsedByComposite: 0 + m_Offset: {x: 0.000000059604645, y: 0} + serializedVersion: 2 + m_Radius: 0.45813966 +--- !u!114 &780000000000000002 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 6388638343240283516} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 0c4fbab01ad40c2449c849cd2a8c620f, type: 3} + m_Name: + m_EditorClassIdentifier: + m_Delegates: + - eventID: 2 + callback: + m_PersistentCalls: + m_Calls: [] + - eventID: 3 + callback: + m_PersistentCalls: + m_Calls: [] --- !u!114 &699543252252589786 MonoBehaviour: m_ObjectHideFlags: 0 diff --git a/Assets/Prefabs/Cut/托盘 (1).prefab b/Assets/Prefabs/Cut/托盘 (1).prefab index 9dbfd0b41..54780c459 100644 --- a/Assets/Prefabs/Cut/托盘 (1).prefab +++ b/Assets/Prefabs/Cut/托盘 (1).prefab @@ -152,51 +152,8 @@ PrefabInstance: m_RemovedComponents: [] m_RemovedGameObjects: [] m_AddedGameObjects: [] - m_AddedComponents: - - targetCorrespondingSourceObject: {fileID: 6388638343240283516, guid: 316c061f3e3c69c439772d3b781e2c74, type: 3} - insertIndex: -1 - addedObject: {fileID: 5066059310206209015} + m_AddedComponents: [] m_SourcePrefab: {fileID: 100100000, guid: 316c061f3e3c69c439772d3b781e2c74, type: 3} ---- !u!1 &2263722661675396300 stripped -GameObject: - m_CorrespondingSourceObject: {fileID: 6388638343240283516, guid: 316c061f3e3c69c439772d3b781e2c74, type: 3} - m_PrefabInstance: {fileID: 5171077393211660720} - m_PrefabAsset: {fileID: 0} ---- !u!58 &5066059310206209015 -CircleCollider2D: - m_ObjectHideFlags: 0 - m_CorrespondingSourceObject: {fileID: 0} - m_PrefabInstance: {fileID: 0} - m_PrefabAsset: {fileID: 0} - m_GameObject: {fileID: 2263722661675396300} - m_Enabled: 1 - m_Density: 1 - m_Material: {fileID: 0} - m_IncludeLayers: - serializedVersion: 2 - m_Bits: 0 - m_ExcludeLayers: - serializedVersion: 2 - m_Bits: 0 - m_LayerOverridePriority: 0 - m_ForceSendLayers: - serializedVersion: 2 - m_Bits: 4294967295 - m_ForceReceiveLayers: - serializedVersion: 2 - m_Bits: 4294967295 - m_ContactCaptureLayers: - serializedVersion: 2 - m_Bits: 4294967295 - m_CallbackLayers: - serializedVersion: 2 - m_Bits: 4294967295 - m_IsTrigger: 1 - m_UsedByEffector: 0 - m_UsedByComposite: 0 - m_Offset: {x: 0.000000059604645, y: 0} - serializedVersion: 2 - m_Radius: 0.45813966 --- !u!4 &7588747131920894494 stripped Transform: m_CorrespondingSourceObject: {fileID: 3356305640573621166, guid: 316c061f3e3c69c439772d3b781e2c74, type: 3} diff --git a/Assets/Scripts/FixSystemNew/BodyModule/CuttingManager.cs b/Assets/Scripts/FixSystemNew/BodyModule/CuttingManager.cs index 1bced0ac0..637a163be 100644 --- a/Assets/Scripts/FixSystemNew/BodyModule/CuttingManager.cs +++ b/Assets/Scripts/FixSystemNew/BodyModule/CuttingManager.cs @@ -34,26 +34,26 @@ namespace AibisDream private void Update() { - if (isCuttingMode && !isGearActive && gearFollower != null) - { - // 检测点击 - if (Input.GetMouseButtonDown(0)) - { - Ray ray = _mainCamera.ScreenPointToRay(Input.mousePosition); - RaycastHit2D hit = Physics2D.Raycast(ray.origin, ray.direction); - - if (hit.collider != null && hit.collider.gameObject.GetComponent() != null) - { - gearFollower=hit.collider.gameObject.GetComponent(); - ActivateGear(); - } - } - } - else if (isCuttingMode && isGearActive && gearFollower != null) + if (isCuttingMode && isGearActive && gearFollower != null) { gearFollower.GearUpdate(); } } + + public bool TryActivateGear(GearFollower source) + { + if (!isCuttingMode || source == null || source != gearFollower) + { + return false; + } + + if (!isGearActive) + { + ActivateGear(); + } + + return isGearActive; + } public void ClearCutLine() { if (gearFollower != null) diff --git a/Assets/Scripts/FixSystemNew/BodyModule/GearFollower.cs b/Assets/Scripts/FixSystemNew/BodyModule/GearFollower.cs index cc008b5bf..78cbe4925 100644 --- a/Assets/Scripts/FixSystemNew/BodyModule/GearFollower.cs +++ b/Assets/Scripts/FixSystemNew/BodyModule/GearFollower.cs @@ -1,14 +1,18 @@ using System.Collections.Generic; +using System.Linq; using UnityEngine; using UnityEngine.Rendering.Universal; using UnityEngine.VFX; +using UnityEngine.EventSystems; using DG.Tweening; using AibisDream.FixSystem; using AibisDream; // 添加OpenSystem的命名空间 +using AibisDream.Framework; using System.Collections; using AibisDream.Kit; -public class GearFollower : MonoBehaviour +[RequireComponent(typeof(EventTriggerEx))] +public class GearFollower : MonoBehaviour, IInteraction { [Header("参数")] public SpriteRenderer targetSpriteRenderer; @@ -95,6 +99,73 @@ public class GearFollower : MonoBehaviour private Vector3 targetPosition = Vector3.zero; private bool isInTractionMode = false; private bool isDrillSfxPlaying = false; // 标记音效是否已播放 + private EventTriggerEx eventTrigger; + private bool isPointerHeld; + + private void Awake() + { + eventTrigger = GetComponent(); + EnsureEventTriggerEntries(); + eventTrigger.Register(EventTriggerType.PointerDown, OnPointerDown); + eventTrigger.Register(EventTriggerType.PointerUp, OnPointerUp); + } + + private void EnsureEventTriggerEntries() + { + if (eventTrigger == null) return; + + var needed = new[] { EventTriggerType.PointerDown, EventTriggerType.PointerUp }; + foreach (var triggerType in needed) + { + if (eventTrigger.triggers.Any(entry => entry.eventID == triggerType)) continue; + eventTrigger.triggers.Add(new EventTrigger.Entry { eventID = triggerType }); + } + } + + private void OnPointerDown(BaseEventData eventData) + { + if (eventData is not PointerEventData { button: PointerEventData.InputButton.Left }) return; + + var eventSystem = EventSystemEx.Instance; + if (!IsAvailable || eventSystem == null || eventSystem.isLocked) + { + eventSystem?.UnRegister(this); + return; + } + + var cuttingManager = FixSystemCenter.SystemDic.Get(); + if (cuttingManager == null || !cuttingManager.TryActivateGear(this)) + { + eventSystem.UnRegister(this); + return; + } + + isPointerHeld = true; + } + + private void OnPointerUp(BaseEventData eventData) + { + if (eventData is not PointerEventData { button: PointerEventData.InputButton.Left }) return; + + isPointerHeld = false; + EndCutting(); + } + + private bool IsInteractionLocked() + { + return EventSystemEx.Instance == null || EventSystemEx.Instance.isLocked; + } + + private void CancelPointerInteraction() + { + isPointerHeld = false; + if (isCutting) + { + EndCutting(); + } + + EventSystemEx.Instance?.UnRegister(this); + } public void Init(SpriteRenderer targetSpriteRenderer) { @@ -146,13 +217,19 @@ public class GearFollower : MonoBehaviour public void GearUpdate() { + if (IsInteractionLocked()) + { + CancelPointerInteraction(); + return; + } + // 获取鼠标在屏幕上的位置 Vector3 mouseScreenPos = Input.mousePosition; // 将屏幕坐标转换为世界坐标 Vector3 mouseWorldPos = mainCamera.ScreenToWorldPoint(new Vector3(mouseScreenPos.x, mouseScreenPos.y, -mainCamera.transform.position.z)); mouseWorldPos.z = 0; // 确保z坐标为0 - if (Input.GetMouseButton(0)) + if (isPointerHeld) { transform.Rotate(Vector3.forward, -360f * Time.deltaTime); } @@ -391,12 +468,12 @@ public class GearFollower : MonoBehaviour EndCutting(); return; } - if (!Input.GetMouseButton(0)) + if (!isPointerHeld) { EndCutting(); return; } - if (!isCutting && Input.GetMouseButton(0)) + if (!isCutting && isPointerHeld) { StartCutting(); } @@ -835,6 +912,13 @@ public class GearFollower : MonoBehaviour private void OnDestroy() { + if (eventTrigger != null) + { + eventTrigger.UnRegister(EventTriggerType.PointerDown, OnPointerDown); + eventTrigger.UnRegister(EventTriggerType.PointerUp, OnPointerUp); + } + EventSystemEx.Instance?.UnRegister(this); + // 清理DOTween序列 if (cutCompleteSequence != null) { @@ -909,6 +993,10 @@ public class GearFollower : MonoBehaviour } } + public bool IsActive => !isCutComplete; + public bool IsAvailable => !isCutComplete; + public GameObject GetGameObject() => gameObject; + // 停止音效的辅助方法 private void StopDrillSfxIfPlaying() {