diff --git a/Assets/AddressableAssetsData/AddressableAssetSettings.asset b/Assets/AddressableAssetsData/AddressableAssetSettings.asset index 8840dbc9b..04056350e 100644 --- a/Assets/AddressableAssetsData/AddressableAssetSettings.asset +++ b/Assets/AddressableAssetsData/AddressableAssetSettings.asset @@ -15,7 +15,7 @@ MonoBehaviour: m_DefaultGroup: 09546aca93a801441859af2a811fe53b m_currentHash: serializedVersion: 2 - Hash: cf6c8ca853b0b2e0e22d46c14944816d + Hash: e92b9b9776d0601449874b8c1b0ed1ae m_OptimizeCatalogSize: 0 m_BuildRemoteCatalog: 0 m_BundleLocalCatalog: 0 diff --git a/Assets/AddressableAssetsData/AssetGroups/Scene_Subway.asset b/Assets/AddressableAssetsData/AssetGroups/Scene_Subway.asset index 8c048b600..ccf825a58 100644 --- a/Assets/AddressableAssetsData/AssetGroups/Scene_Subway.asset +++ b/Assets/AddressableAssetsData/AssetGroups/Scene_Subway.asset @@ -22,23 +22,8 @@ MonoBehaviour: m_ReadOnly: 0 m_SerializedLabels: [] FlaggedDuringContentUpdateRestriction: 0 - - m_GUID: 2d4ccf1c582289344976ab5c3af05515 - m_Address: "Animation/\u5730\u94C1\u533B\u751F" - m_ReadOnly: 0 - m_SerializedLabels: [] - FlaggedDuringContentUpdateRestriction: 0 - - m_GUID: 0057bf4b5fe16aa4b8c7db27d0ddfc0a - m_Address: "Animation/\u5730\u94C1\u706B\u5C71" - m_ReadOnly: 0 - m_SerializedLabels: [] - FlaggedDuringContentUpdateRestriction: 0 - - m_GUID: 40f07326ace570241bd93fcfc3decb87 - m_Address: "Animation/\u5730\u94C1\u706B\u5C71\u6697" - m_ReadOnly: 0 - m_SerializedLabels: [] - FlaggedDuringContentUpdateRestriction: 0 - - m_GUID: a63b52b3e8d9e0441aaff086b9c1ae6b - m_Address: "Animation/\u5730\u94C1\u533B\u751F\u6697" + - m_GUID: b0ee8d48dab31814da87651bba9b5041 + m_Address: "FrameAnimation/\u5730\u94C1\u533B\u751F" m_ReadOnly: 0 m_SerializedLabels: [] FlaggedDuringContentUpdateRestriction: 0 @@ -57,6 +42,11 @@ MonoBehaviour: m_ReadOnly: 0 m_SerializedLabels: [] FlaggedDuringContentUpdateRestriction: 0 + - m_GUID: b02d1e0ae4bc971408a0b6a667597624 + m_Address: "FrameAnimation/\u5730\u94C1\u706B\u5C71" + m_ReadOnly: 0 + m_SerializedLabels: [] + FlaggedDuringContentUpdateRestriction: 0 m_ReadOnly: 0 m_Settings: {fileID: 11400000, guid: 77169ce22e430f64fb36c771815a4a7b, type: 2} m_SchemaSet: diff --git a/Assets/Editor/AutoNextMetadataTests.cs b/Assets/Editor/AutoNextMetadataTests.cs new file mode 100644 index 000000000..39be85165 --- /dev/null +++ b/Assets/Editor/AutoNextMetadataTests.cs @@ -0,0 +1,123 @@ +using System.Collections.Generic; +using System.Globalization; +using System.Text.RegularExpressions; +using AibisDream.Utility; +using NUnit.Framework; +using UnityEngine; +using UnityEngine.TestTools; +using Yarn.Markup; +using Yarn.Unity; + +namespace AibisDream.SystemEditor.Tests +{ + public sealed class AutoNextMetadataTests + { + [Test] + public void AutoNextWithoutParameter_UsesExistingFixedDelay() + { + var lineInfo = LineInfo.Generate(CreateLine("auto_next")); + + Assert.That(lineInfo.isAutoSkip, Is.True); + Assert.That(lineInfo.autoNextDelaySeconds, Is.Null); + Assert.That(lineInfo.CalcAutoNextDelayTime(), Is.EqualTo(ConstRef.FixedDelay)); + } + + [Test] + public void AutoNextWithParameter_UsesSpecifiedSeconds() + { + var lineInfo = LineInfo.Generate(CreateLine("auto_next:4.5")); + + Assert.That(lineInfo.isAutoSkip, Is.True); + Assert.That(lineInfo.autoNextDelaySeconds, Is.EqualTo(4.5f)); + Assert.That(lineInfo.CalcAutoNextDelayTime(), Is.EqualTo(4500)); + } + + [Test] + public void AutoNextWithZeroDelay_IsValid() + { + var lineInfo = LineInfo.Generate(CreateLine("auto_next:0")); + + Assert.That(lineInfo.isAutoSkip, Is.True); + Assert.That(lineInfo.autoNextDelaySeconds, Is.EqualTo(0f)); + Assert.That(lineInfo.CalcAutoNextDelayTime(), Is.Zero); + } + + [Test] + public void AutoNextParameter_UsesInvariantCulture() + { + var originalCulture = CultureInfo.CurrentCulture; + try + { + CultureInfo.CurrentCulture = new CultureInfo("de-DE"); + var lineInfo = LineInfo.Generate(CreateLine("auto_next:4.5")); + + Assert.That(lineInfo.autoNextDelaySeconds, Is.EqualTo(4.5f)); + Assert.That(lineInfo.CalcAutoNextDelayTime(), Is.EqualTo(4500)); + } + finally + { + CultureInfo.CurrentCulture = originalCulture; + } + } + + [TestCase("auto_next:")] + [TestCase("auto_next:abc")] + [TestCase("auto_next:-1")] + [TestCase("auto_next:NaN")] + [TestCase("auto_next:Infinity")] + [TestCase("auto_next:2147484")] + public void AutoNextWithInvalidParameter_FallsBackToExistingFixedDelay(string metadata) + { + LogAssert.Expect(LogType.Warning, new Regex("auto_next 参数无效")); + + var lineInfo = LineInfo.Generate(CreateLine(metadata)); + + Assert.That(lineInfo.isAutoSkip, Is.True); + Assert.That(lineInfo.autoNextDelaySeconds, Is.Null); + Assert.That(lineInfo.CalcAutoNextDelayTime(), Is.EqualTo(ConstRef.FixedDelay)); + } + + [TestCase("auto_next_extra")] + [TestCase("auto_next_extra:4.5")] + [TestCase("AUTO_NEXT")] + [TestCase("AUTO_NEXT:4.5")] + public void SimilarMetadata_IsNotRecognized(string metadata) + { + var line = CreateLine(metadata); + + Assert.That(line.IsAutoSkipLine(), Is.False); + Assert.That(line.TryGetAutoNextDelaySeconds(out _), Is.False); + } + + [Test] + public void ParameterizedTag_TakesPrecedenceOverPlainTag() + { + var lineInfo = LineInfo.Generate(CreateLine("auto_next", "auto_next:2.5")); + + Assert.That(lineInfo.isAutoSkip, Is.True); + Assert.That(lineInfo.autoNextDelaySeconds, Is.EqualTo(2.5f)); + Assert.That(lineInfo.CalcAutoNextDelayTime(), Is.EqualTo(2500)); + } + + [Test] + public void MultipleParameterizedTags_UseFirstAndWarn() + { + LogAssert.Expect(LogType.Warning, new Regex("存在多个 auto_next 参数标签")); + + var lineInfo = LineInfo.Generate(CreateLine("auto_next:2.5", "auto_next:4.5")); + + Assert.That(lineInfo.autoNextDelaySeconds, Is.EqualTo(2.5f)); + Assert.That(lineInfo.CalcAutoNextDelayTime(), Is.EqualTo(2500)); + } + + private static LocalizedLine CreateLine(params string[] metadata) + { + return new LocalizedLine + { + TextID = "line:auto-next-test", + Metadata = metadata, + Text = new MarkupParseResult("Test line", new List()) + }; + } + } +} diff --git a/Assets/Editor/AutoNextMetadataTests.cs.meta b/Assets/Editor/AutoNextMetadataTests.cs.meta new file mode 100644 index 000000000..dca6be407 --- /dev/null +++ b/Assets/Editor/AutoNextMetadataTests.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: d476e493adf4855468ce46409dae6661 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Editor/FrameAnimation/FrameAnimationEditorServices.cs b/Assets/Editor/FrameAnimation/FrameAnimationEditorServices.cs index 53ca236e8..d9434ca82 100644 --- a/Assets/Editor/FrameAnimation/FrameAnimationEditorServices.cs +++ b/Assets/Editor/FrameAnimation/FrameAnimationEditorServices.cs @@ -122,7 +122,7 @@ namespace AibisDream.FrameAnimation.Editor { if (clip == null || !clip.IsImported) { - return "Manual"; + return clip != null && clip.HasStandaloneImportSource ? "Standalone Source" : "Manual"; } var sourceId = clip.ImportInfo?.ImportSourceId; if (sourceNames != null && !string.IsNullOrEmpty(sourceId) && @@ -232,7 +232,7 @@ namespace AibisDream.FrameAnimation.Editor if (clip.IsImported || AssetDatabase.IsSubAsset(clip) || string.IsNullOrEmpty(AssetDatabase.GetAssetPath(clip))) { - error = "只能添加独立 .asset 形式的 Manual Clip。"; + error = "只能添加独立 .asset 形式、且不由 Graph ImportSource 管理的 Clip。"; return false; } if (graph.Clips.Contains(clip)) diff --git a/Assets/Editor/FrameAnimation/FrameAnimationGraphAuthoringServices.cs b/Assets/Editor/FrameAnimation/FrameAnimationGraphAuthoringServices.cs index aa2216769..764892ae4 100644 --- a/Assets/Editor/FrameAnimation/FrameAnimationGraphAuthoringServices.cs +++ b/Assets/Editor/FrameAnimation/FrameAnimationGraphAuthoringServices.cs @@ -403,7 +403,7 @@ namespace AibisDream.FrameAnimation.Editor public static string MakeUniqueFlowId(FrameAnimationGraph graph, AnimationNode node) { - var root = (string.IsNullOrWhiteSpace(node?.DisplayName) ? "Animation" : node.DisplayName) + "Flow"; + var root = (string.IsNullOrWhiteSpace(node?.DisplayName) ? "Animation" : node.DisplayName) + "_Flow"; var candidate = root; var suffix = 2; while (!FrameAnimationAssetOperations.IsPlayableIdAvailable(graph, candidate)) diff --git a/Assets/Editor/FrameAnimation/FrameAnimationGraphEditorWindow.cs b/Assets/Editor/FrameAnimation/FrameAnimationGraphEditorWindow.cs index e833ab639..6e7533ab6 100644 --- a/Assets/Editor/FrameAnimation/FrameAnimationGraphEditorWindow.cs +++ b/Assets/Editor/FrameAnimation/FrameAnimationGraphEditorWindow.cs @@ -64,6 +64,7 @@ namespace AibisDream.FrameAnimation.Editor private SerializedObject clipSerializedObject; private FrameClip frameListClip; private ReorderableList frameList; + private bool frameListEditable; private readonly FrameAnimationPreviewCoordinator previewCoordinator = new FrameAnimationPreviewCoordinator(); private readonly List previewTimelineSliders = new List(); private readonly List