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/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 386ad3310..a77ecc0dc 100644
Binary files a/Assets/GameContent/Scene_ClincOut/Actor/佩佩/外出佩佩动画.aseprite and b/Assets/GameContent/Scene_ClincOut/Actor/佩佩/外出佩佩动画.aseprite differ
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:
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/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/Scenes/Persistence.unity b/Assets/Scenes/Persistence.unity
index f849275ec..0aa155a11 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: []
@@ -8728,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
@@ -8812,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/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()
{
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;
}
}