Compare commits
3
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
9cda02f02b | ||
|
|
97ae87372d | ||
|
|
8671179ef4 |
@@ -67,17 +67,6 @@ Modular framework with Kit-pattern utilities:
|
||||
4. Favor composition over inheritance
|
||||
5. All runtime asset loading via `ResourceSystem` — never `Resources.Load`
|
||||
|
||||
### Unity Tests
|
||||
|
||||
- Unity Tests are encouraged when they provide useful feedback, but do not add tests mechanically at every layer merely because production code changed.
|
||||
- Choose the lowest layer that fully protects each behavior. Add a second layer only when it covers a distinct integration risk.
|
||||
- Stable tests protect accepted contracts and high-risk regressions. Prefer extending an existing domain suite; create a new permanent file or asmdef only for a genuinely new long-lived boundary.
|
||||
- Tests for unfinished, exploratory, or intermediate behavior must live under `Assets/Tests/_Temporary/<Feature>/`; never mix them into permanent suites.
|
||||
- Every temporary test folder must state its purpose and deletion or promotion condition. Delete it when the feature is accepted, replaced, abandoned, or adequately covered by permanent contract tests.
|
||||
- Prefer EditMode when it is sufficient; use PlayMode for behavior that truly requires Unity lifecycle, frames, coroutines, physics, or Timeline runtime integration.
|
||||
- Remove tests when their feature is removed, their contract is replaced, or they only mirror implementation details.
|
||||
- Follow [Unity Test Guidelines](Docs/UnityTestingGuidelines.md) for placement, design, cleanup, and review criteria.
|
||||
|
||||
### Scene Structure
|
||||
- Scenes in `Assets/Scenes/`; `Persistence` scene hosts global managers and Persistent Loader
|
||||
- Fix scenes: `{Character}FixScene` (e.g. `HuoShanFixScene`, `PeipeiFixScene`, `ExpressFixScene`)
|
||||
|
||||
@@ -15,7 +15,7 @@ MonoBehaviour:
|
||||
m_DefaultGroup: 09546aca93a801441859af2a811fe53b
|
||||
m_currentHash:
|
||||
serializedVersion: 2
|
||||
Hash: 2b0c2e120ca52edab004ec49752fe5ec
|
||||
Hash: ae2a22805ec7ff55174c38995aa3e54d
|
||||
m_OptimizeCatalogSize: 0
|
||||
m_BuildRemoteCatalog: 0
|
||||
m_BundleLocalCatalog: 0
|
||||
@@ -71,16 +71,6 @@ MonoBehaviour:
|
||||
- {fileID: 11400000, guid: 089fc86f714d6a94bafea3b117fbd639, type: 2}
|
||||
- {fileID: 11400000, guid: 1c592abe924a856439461f3785ec53da, type: 2}
|
||||
- {fileID: 11400000, guid: 26fd03652560e7f4abec092229fc38f2, type: 2}
|
||||
- {fileID: 11400000, guid: 1051221ba75fcea47b67648acedc5383, type: 2}
|
||||
- {fileID: 11400000, guid: 2b05cac5050c285498c6d11b749f865a, type: 2}
|
||||
- {fileID: 11400000, guid: 92e4c8e9ee367dd499b7e2f0c1e4fb3a, type: 2}
|
||||
- {fileID: 11400000, guid: 2bf1c0c3a87916a41aa95e5c29efe3d9, type: 2}
|
||||
- {fileID: 11400000, guid: f91efa4c655c0014ca776fb2e8b4f1fc, type: 2}
|
||||
- {fileID: 11400000, guid: b05cfdc6bbe312248b8be84a1ff01e32, type: 2}
|
||||
- {fileID: 11400000, guid: 6e0c8dfd5e5e1c74b80cecc21df2d9cd, type: 2}
|
||||
- {fileID: 11400000, guid: d029169cdb68ba848a0f0260aeddae55, type: 2}
|
||||
- {fileID: 11400000, guid: 0418ce3bee30b98409aa4d268d7d89d7, type: 2}
|
||||
- {fileID: 11400000, guid: f0205c268777acb459dddbb4690d7c27, type: 2}
|
||||
m_BuildSettings:
|
||||
m_CompileScriptsInVirtualMode: 0
|
||||
m_CleanupStreamingAssetsAfterBuilds: 1
|
||||
@@ -131,7 +121,6 @@ MonoBehaviour:
|
||||
- Locale-pt-BR
|
||||
- Locale-ru
|
||||
- Locale-es
|
||||
- Typography
|
||||
m_SchemaTemplates: []
|
||||
m_GroupTemplateObjects:
|
||||
- {fileID: 11400000, guid: 0c158052680a3e94184c450d00291514, type: 2}
|
||||
|
||||
-31
@@ -1,31 +0,0 @@
|
||||
%YAML 1.1
|
||||
%TAG !u! tag:unity3d.com,2011:
|
||||
--- !u!114 &11400000
|
||||
MonoBehaviour:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 0}
|
||||
m_Enabled: 1
|
||||
m_EditorHideFlags: 0
|
||||
m_Script: {fileID: 11500000, guid: bbb281ee3bf0b054c82ac2347e9e782c, type: 3}
|
||||
m_Name: Localization-Asset-Tables-Portuguese (Brazil) (pt-BR)
|
||||
m_EditorClassIdentifier:
|
||||
m_GroupName: Localization-Asset-Tables-Portuguese (Brazil) (pt-BR)
|
||||
m_Data:
|
||||
m_SerializedData: []
|
||||
m_GUID: 96edbabcf99a9a74ab2ac2cffeea0541
|
||||
m_SerializeEntries:
|
||||
- m_GUID: 90b94df062e41984db3329bb28f34faf
|
||||
m_Address: sprite_pt-BR
|
||||
m_ReadOnly: 1
|
||||
m_SerializedLabels:
|
||||
- Locale-pt-BR
|
||||
FlaggedDuringContentUpdateRestriction: 0
|
||||
m_ReadOnly: 1
|
||||
m_Settings: {fileID: 11400000, guid: 77169ce22e430f64fb36c771815a4a7b, type: 2}
|
||||
m_SchemaSet:
|
||||
m_Schemas:
|
||||
- {fileID: 11400000, guid: 29e8470e79377b4409a648ee18ce7682, type: 2}
|
||||
- {fileID: 11400000, guid: 04b28f4d862df2440ac0be17bd1cd694, type: 2}
|
||||
@@ -1,31 +0,0 @@
|
||||
%YAML 1.1
|
||||
%TAG !u! tag:unity3d.com,2011:
|
||||
--- !u!114 &11400000
|
||||
MonoBehaviour:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 0}
|
||||
m_Enabled: 1
|
||||
m_EditorHideFlags: 0
|
||||
m_Script: {fileID: 11500000, guid: bbb281ee3bf0b054c82ac2347e9e782c, type: 3}
|
||||
m_Name: Localization-Asset-Tables-Russian (ru)
|
||||
m_EditorClassIdentifier:
|
||||
m_GroupName: Localization-Asset-Tables-Russian (ru)
|
||||
m_Data:
|
||||
m_SerializedData: []
|
||||
m_GUID: dc30b2d9e3b8f714aa238ad780039c50
|
||||
m_SerializeEntries:
|
||||
- m_GUID: 9905fa50dcc341244b3e840602e2be5c
|
||||
m_Address: sprite_ru
|
||||
m_ReadOnly: 1
|
||||
m_SerializedLabels:
|
||||
- Locale-ru
|
||||
FlaggedDuringContentUpdateRestriction: 0
|
||||
m_ReadOnly: 1
|
||||
m_Settings: {fileID: 11400000, guid: 77169ce22e430f64fb36c771815a4a7b, type: 2}
|
||||
m_SchemaSet:
|
||||
m_Schemas:
|
||||
- {fileID: 11400000, guid: ffaea4b400f30fd43bb702a14172406c, type: 2}
|
||||
- {fileID: 11400000, guid: 42d59bddf89a08140adb832b309cf5a7, type: 2}
|
||||
@@ -1,31 +0,0 @@
|
||||
%YAML 1.1
|
||||
%TAG !u! tag:unity3d.com,2011:
|
||||
--- !u!114 &11400000
|
||||
MonoBehaviour:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 0}
|
||||
m_Enabled: 1
|
||||
m_EditorHideFlags: 0
|
||||
m_Script: {fileID: 11500000, guid: bbb281ee3bf0b054c82ac2347e9e782c, type: 3}
|
||||
m_Name: Localization-Asset-Tables-Spanish (es)
|
||||
m_EditorClassIdentifier:
|
||||
m_GroupName: Localization-Asset-Tables-Spanish (es)
|
||||
m_Data:
|
||||
m_SerializedData: []
|
||||
m_GUID: 763871518508ac74989cadb20512c476
|
||||
m_SerializeEntries:
|
||||
- m_GUID: d4d54e71333c6a84796e814143a6ecb2
|
||||
m_Address: sprite_es
|
||||
m_ReadOnly: 1
|
||||
m_SerializedLabels:
|
||||
- Locale-es
|
||||
FlaggedDuringContentUpdateRestriction: 0
|
||||
m_ReadOnly: 1
|
||||
m_Settings: {fileID: 11400000, guid: 77169ce22e430f64fb36c771815a4a7b, type: 2}
|
||||
m_SchemaSet:
|
||||
m_Schemas:
|
||||
- {fileID: 11400000, guid: 779a71a2538b963459fd1ef9cc055b7c, type: 2}
|
||||
- {fileID: 11400000, guid: 90fabd6fb037ad0489aaccead4298af4, type: 2}
|
||||
+7
-1
@@ -16,7 +16,13 @@ MonoBehaviour:
|
||||
m_Data:
|
||||
m_SerializedData: []
|
||||
m_GUID: b848f030a719ba04c9f1e343f17ddc0c
|
||||
m_SerializeEntries: []
|
||||
m_SerializeEntries:
|
||||
- m_GUID: 6d8ee72f781acf64abfe82ab659f5771
|
||||
m_Address: 03_qq_symbol
|
||||
m_ReadOnly: 1
|
||||
m_SerializedLabels:
|
||||
- Locale-zh-Hans
|
||||
FlaggedDuringContentUpdateRestriction: 0
|
||||
m_ReadOnly: 1
|
||||
m_Settings: {fileID: 11400000, guid: 77169ce22e430f64fb36c771815a4a7b, type: 2}
|
||||
m_SchemaSet:
|
||||
|
||||
-25
@@ -1,25 +0,0 @@
|
||||
%YAML 1.1
|
||||
%TAG !u! tag:unity3d.com,2011:
|
||||
--- !u!114 &11400000
|
||||
MonoBehaviour:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 0}
|
||||
m_Enabled: 1
|
||||
m_EditorHideFlags: 0
|
||||
m_Script: {fileID: 11500000, guid: bbb281ee3bf0b054c82ac2347e9e782c, type: 3}
|
||||
m_Name: Localization-Assets-Japanese (Japan) (ja-JP)
|
||||
m_EditorClassIdentifier:
|
||||
m_GroupName: Localization-Assets-Japanese (Japan) (ja-JP)
|
||||
m_Data:
|
||||
m_SerializedData: []
|
||||
m_GUID: 9bb3a2bc8a719b04b918354595af2429
|
||||
m_SerializeEntries: []
|
||||
m_ReadOnly: 1
|
||||
m_Settings: {fileID: 11400000, guid: 77169ce22e430f64fb36c771815a4a7b, type: 2}
|
||||
m_SchemaSet:
|
||||
m_Schemas:
|
||||
- {fileID: 11400000, guid: d3a6468425a79364fa21d96e369e8399, type: 2}
|
||||
- {fileID: 11400000, guid: 2a43b55ded538f14f92548425cfb42e5, type: 2}
|
||||
@@ -40,33 +40,18 @@ MonoBehaviour:
|
||||
m_ReadOnly: 1
|
||||
m_SerializedLabels: []
|
||||
FlaggedDuringContentUpdateRestriction: 0
|
||||
- m_GUID: 539db7533ab287b45a774b3c5e2ccd9d
|
||||
m_Address: "\u7ED3\u5C3E\u56FE\u6807"
|
||||
- m_GUID: 032b1b5e09139ae45b6de51d35baae79
|
||||
m_Address: discord-square-color-icon
|
||||
m_ReadOnly: 1
|
||||
m_SerializedLabels:
|
||||
- Locale-en
|
||||
- Locale-zh-Hans
|
||||
- Locale-ja-JP
|
||||
- Locale-pt-BR
|
||||
- Locale-ru
|
||||
- Locale-es
|
||||
FlaggedDuringContentUpdateRestriction: 0
|
||||
- m_GUID: 7ea8c68fdc624de4c86434963c06ec55
|
||||
m_Address: Assets/Language/ChapterInfo/ChapterInfo Shared Data.asset
|
||||
m_ReadOnly: 1
|
||||
m_SerializedLabels: []
|
||||
FlaggedDuringContentUpdateRestriction: 0
|
||||
- m_GUID: e93fefacb613c7949bd980f442308be8
|
||||
m_Address: "\u613F\u671B\u5355\u95EE\u5377\u793E\u7FA4"
|
||||
m_ReadOnly: 1
|
||||
m_SerializedLabels:
|
||||
- Locale-zh-Hans
|
||||
- Locale-en
|
||||
- Locale-ja-JP
|
||||
- Locale-pt-BR
|
||||
- Locale-ru
|
||||
- Locale-es
|
||||
FlaggedDuringContentUpdateRestriction: 0
|
||||
m_ReadOnly: 1
|
||||
m_Settings: {fileID: 11400000, guid: 77169ce22e430f64fb36c771815a4a7b, type: 2}
|
||||
m_SchemaSet:
|
||||
|
||||
-45
@@ -1,45 +0,0 @@
|
||||
%YAML 1.1
|
||||
%TAG !u! tag:unity3d.com,2011:
|
||||
--- !u!114 &11400000
|
||||
MonoBehaviour:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 0}
|
||||
m_Enabled: 1
|
||||
m_EditorHideFlags: 0
|
||||
m_Script: {fileID: 11500000, guid: e5d17a21594effb4e9591490b009e7aa, type: 3}
|
||||
m_Name: Localization-Asset-Tables-Portuguese (Brazil) (pt-BR)_BundledAssetGroupSchema
|
||||
m_EditorClassIdentifier:
|
||||
m_Group: {fileID: 11400000, guid: 92e4c8e9ee367dd499b7e2f0c1e4fb3a, type: 2}
|
||||
m_InternalBundleIdMode: 1
|
||||
m_Compression: 1
|
||||
m_IncludeAddressInCatalog: 1
|
||||
m_IncludeGUIDInCatalog: 1
|
||||
m_IncludeLabelsInCatalog: 1
|
||||
m_InternalIdNamingMode: 0
|
||||
m_CacheClearBehavior: 0
|
||||
m_IncludeInBuild: 1
|
||||
m_BundledAssetProviderType:
|
||||
m_AssemblyName: Unity.ResourceManager, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null
|
||||
m_ClassName: UnityEngine.ResourceManagement.ResourceProviders.BundledAssetProvider
|
||||
m_ForceUniqueProvider: 0
|
||||
m_UseAssetBundleCache: 1
|
||||
m_UseAssetBundleCrc: 1
|
||||
m_UseAssetBundleCrcForCachedBundles: 1
|
||||
m_UseUWRForLocalBundles: 0
|
||||
m_Timeout: 0
|
||||
m_ChunkedTransfer: 0
|
||||
m_RedirectLimit: -1
|
||||
m_RetryCount: 0
|
||||
m_BuildPath:
|
||||
m_Id: 2ae6a7549097a9b4caefe44b61b1fcc5
|
||||
m_LoadPath:
|
||||
m_Id: 631ec5c677ab8084fb9be9b9ee39c6ef
|
||||
m_BundleMode: 0
|
||||
m_AssetBundleProviderType:
|
||||
m_AssemblyName: Unity.ResourceManager, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null
|
||||
m_ClassName: UnityEngine.ResourceManagement.ResourceProviders.AssetBundleProvider
|
||||
m_BundleNaming: 1
|
||||
m_AssetLoadMode: 0
|
||||
-8
@@ -1,8 +0,0 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 29e8470e79377b4409a648ee18ce7682
|
||||
NativeFormatImporter:
|
||||
externalObjects: {}
|
||||
mainObjectFileID: 11400000
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
-16
@@ -1,16 +0,0 @@
|
||||
%YAML 1.1
|
||||
%TAG !u! tag:unity3d.com,2011:
|
||||
--- !u!114 &11400000
|
||||
MonoBehaviour:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 0}
|
||||
m_Enabled: 1
|
||||
m_EditorHideFlags: 0
|
||||
m_Script: {fileID: 11500000, guid: 5834b5087d578d24c926ce20cd31e6d6, type: 3}
|
||||
m_Name: Localization-Asset-Tables-Portuguese (Brazil) (pt-BR)_ContentUpdateGroupSchema
|
||||
m_EditorClassIdentifier:
|
||||
m_Group: {fileID: 11400000, guid: 92e4c8e9ee367dd499b7e2f0c1e4fb3a, type: 2}
|
||||
m_StaticContent: 0
|
||||
-8
@@ -1,8 +0,0 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 04b28f4d862df2440ac0be17bd1cd694
|
||||
NativeFormatImporter:
|
||||
externalObjects: {}
|
||||
mainObjectFileID: 11400000
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
-45
@@ -1,45 +0,0 @@
|
||||
%YAML 1.1
|
||||
%TAG !u! tag:unity3d.com,2011:
|
||||
--- !u!114 &11400000
|
||||
MonoBehaviour:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 0}
|
||||
m_Enabled: 1
|
||||
m_EditorHideFlags: 0
|
||||
m_Script: {fileID: 11500000, guid: e5d17a21594effb4e9591490b009e7aa, type: 3}
|
||||
m_Name: Localization-Asset-Tables-Russian (ru)_BundledAssetGroupSchema
|
||||
m_EditorClassIdentifier:
|
||||
m_Group: {fileID: 11400000, guid: 2bf1c0c3a87916a41aa95e5c29efe3d9, type: 2}
|
||||
m_InternalBundleIdMode: 1
|
||||
m_Compression: 1
|
||||
m_IncludeAddressInCatalog: 1
|
||||
m_IncludeGUIDInCatalog: 1
|
||||
m_IncludeLabelsInCatalog: 1
|
||||
m_InternalIdNamingMode: 0
|
||||
m_CacheClearBehavior: 0
|
||||
m_IncludeInBuild: 1
|
||||
m_BundledAssetProviderType:
|
||||
m_AssemblyName: Unity.ResourceManager, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null
|
||||
m_ClassName: UnityEngine.ResourceManagement.ResourceProviders.BundledAssetProvider
|
||||
m_ForceUniqueProvider: 0
|
||||
m_UseAssetBundleCache: 1
|
||||
m_UseAssetBundleCrc: 1
|
||||
m_UseAssetBundleCrcForCachedBundles: 1
|
||||
m_UseUWRForLocalBundles: 0
|
||||
m_Timeout: 0
|
||||
m_ChunkedTransfer: 0
|
||||
m_RedirectLimit: -1
|
||||
m_RetryCount: 0
|
||||
m_BuildPath:
|
||||
m_Id: 2ae6a7549097a9b4caefe44b61b1fcc5
|
||||
m_LoadPath:
|
||||
m_Id: 631ec5c677ab8084fb9be9b9ee39c6ef
|
||||
m_BundleMode: 0
|
||||
m_AssetBundleProviderType:
|
||||
m_AssemblyName: Unity.ResourceManager, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null
|
||||
m_ClassName: UnityEngine.ResourceManagement.ResourceProviders.AssetBundleProvider
|
||||
m_BundleNaming: 1
|
||||
m_AssetLoadMode: 0
|
||||
-8
@@ -1,8 +0,0 @@
|
||||
fileFormatVersion: 2
|
||||
guid: ffaea4b400f30fd43bb702a14172406c
|
||||
NativeFormatImporter:
|
||||
externalObjects: {}
|
||||
mainObjectFileID: 11400000
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
-16
@@ -1,16 +0,0 @@
|
||||
%YAML 1.1
|
||||
%TAG !u! tag:unity3d.com,2011:
|
||||
--- !u!114 &11400000
|
||||
MonoBehaviour:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 0}
|
||||
m_Enabled: 1
|
||||
m_EditorHideFlags: 0
|
||||
m_Script: {fileID: 11500000, guid: 5834b5087d578d24c926ce20cd31e6d6, type: 3}
|
||||
m_Name: Localization-Asset-Tables-Russian (ru)_ContentUpdateGroupSchema
|
||||
m_EditorClassIdentifier:
|
||||
m_Group: {fileID: 11400000, guid: 2bf1c0c3a87916a41aa95e5c29efe3d9, type: 2}
|
||||
m_StaticContent: 0
|
||||
-8
@@ -1,8 +0,0 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 42d59bddf89a08140adb832b309cf5a7
|
||||
NativeFormatImporter:
|
||||
externalObjects: {}
|
||||
mainObjectFileID: 11400000
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
-45
@@ -1,45 +0,0 @@
|
||||
%YAML 1.1
|
||||
%TAG !u! tag:unity3d.com,2011:
|
||||
--- !u!114 &11400000
|
||||
MonoBehaviour:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 0}
|
||||
m_Enabled: 1
|
||||
m_EditorHideFlags: 0
|
||||
m_Script: {fileID: 11500000, guid: e5d17a21594effb4e9591490b009e7aa, type: 3}
|
||||
m_Name: Localization-Asset-Tables-Spanish (es)_BundledAssetGroupSchema
|
||||
m_EditorClassIdentifier:
|
||||
m_Group: {fileID: 11400000, guid: f91efa4c655c0014ca776fb2e8b4f1fc, type: 2}
|
||||
m_InternalBundleIdMode: 1
|
||||
m_Compression: 1
|
||||
m_IncludeAddressInCatalog: 1
|
||||
m_IncludeGUIDInCatalog: 1
|
||||
m_IncludeLabelsInCatalog: 1
|
||||
m_InternalIdNamingMode: 0
|
||||
m_CacheClearBehavior: 0
|
||||
m_IncludeInBuild: 1
|
||||
m_BundledAssetProviderType:
|
||||
m_AssemblyName: Unity.ResourceManager, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null
|
||||
m_ClassName: UnityEngine.ResourceManagement.ResourceProviders.BundledAssetProvider
|
||||
m_ForceUniqueProvider: 0
|
||||
m_UseAssetBundleCache: 1
|
||||
m_UseAssetBundleCrc: 1
|
||||
m_UseAssetBundleCrcForCachedBundles: 1
|
||||
m_UseUWRForLocalBundles: 0
|
||||
m_Timeout: 0
|
||||
m_ChunkedTransfer: 0
|
||||
m_RedirectLimit: -1
|
||||
m_RetryCount: 0
|
||||
m_BuildPath:
|
||||
m_Id: 2ae6a7549097a9b4caefe44b61b1fcc5
|
||||
m_LoadPath:
|
||||
m_Id: 631ec5c677ab8084fb9be9b9ee39c6ef
|
||||
m_BundleMode: 0
|
||||
m_AssetBundleProviderType:
|
||||
m_AssemblyName: Unity.ResourceManager, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null
|
||||
m_ClassName: UnityEngine.ResourceManagement.ResourceProviders.AssetBundleProvider
|
||||
m_BundleNaming: 1
|
||||
m_AssetLoadMode: 0
|
||||
-8
@@ -1,8 +0,0 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 779a71a2538b963459fd1ef9cc055b7c
|
||||
NativeFormatImporter:
|
||||
externalObjects: {}
|
||||
mainObjectFileID: 11400000
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
-16
@@ -1,16 +0,0 @@
|
||||
%YAML 1.1
|
||||
%TAG !u! tag:unity3d.com,2011:
|
||||
--- !u!114 &11400000
|
||||
MonoBehaviour:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 0}
|
||||
m_Enabled: 1
|
||||
m_EditorHideFlags: 0
|
||||
m_Script: {fileID: 11500000, guid: 5834b5087d578d24c926ce20cd31e6d6, type: 3}
|
||||
m_Name: Localization-Asset-Tables-Spanish (es)_ContentUpdateGroupSchema
|
||||
m_EditorClassIdentifier:
|
||||
m_Group: {fileID: 11400000, guid: f91efa4c655c0014ca776fb2e8b4f1fc, type: 2}
|
||||
m_StaticContent: 0
|
||||
-8
@@ -1,8 +0,0 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 90fabd6fb037ad0489aaccead4298af4
|
||||
NativeFormatImporter:
|
||||
externalObjects: {}
|
||||
mainObjectFileID: 11400000
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
-45
@@ -1,45 +0,0 @@
|
||||
%YAML 1.1
|
||||
%TAG !u! tag:unity3d.com,2011:
|
||||
--- !u!114 &11400000
|
||||
MonoBehaviour:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 0}
|
||||
m_Enabled: 1
|
||||
m_EditorHideFlags: 0
|
||||
m_Script: {fileID: 11500000, guid: e5d17a21594effb4e9591490b009e7aa, type: 3}
|
||||
m_Name: Localization-Assets-Japanese (Japan) (ja-JP)_BundledAssetGroupSchema
|
||||
m_EditorClassIdentifier:
|
||||
m_Group: {fileID: 11400000, guid: 2b05cac5050c285498c6d11b749f865a, type: 2}
|
||||
m_InternalBundleIdMode: 1
|
||||
m_Compression: 1
|
||||
m_IncludeAddressInCatalog: 1
|
||||
m_IncludeGUIDInCatalog: 1
|
||||
m_IncludeLabelsInCatalog: 1
|
||||
m_InternalIdNamingMode: 0
|
||||
m_CacheClearBehavior: 0
|
||||
m_IncludeInBuild: 1
|
||||
m_BundledAssetProviderType:
|
||||
m_AssemblyName: Unity.ResourceManager, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null
|
||||
m_ClassName: UnityEngine.ResourceManagement.ResourceProviders.BundledAssetProvider
|
||||
m_ForceUniqueProvider: 0
|
||||
m_UseAssetBundleCache: 1
|
||||
m_UseAssetBundleCrc: 1
|
||||
m_UseAssetBundleCrcForCachedBundles: 1
|
||||
m_UseUWRForLocalBundles: 0
|
||||
m_Timeout: 0
|
||||
m_ChunkedTransfer: 0
|
||||
m_RedirectLimit: -1
|
||||
m_RetryCount: 0
|
||||
m_BuildPath:
|
||||
m_Id: 2ae6a7549097a9b4caefe44b61b1fcc5
|
||||
m_LoadPath:
|
||||
m_Id: 631ec5c677ab8084fb9be9b9ee39c6ef
|
||||
m_BundleMode: 0
|
||||
m_AssetBundleProviderType:
|
||||
m_AssemblyName: Unity.ResourceManager, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null
|
||||
m_ClassName: UnityEngine.ResourceManagement.ResourceProviders.AssetBundleProvider
|
||||
m_BundleNaming: 1
|
||||
m_AssetLoadMode: 0
|
||||
-8
@@ -1,8 +0,0 @@
|
||||
fileFormatVersion: 2
|
||||
guid: d3a6468425a79364fa21d96e369e8399
|
||||
NativeFormatImporter:
|
||||
externalObjects: {}
|
||||
mainObjectFileID: 11400000
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
-16
@@ -1,16 +0,0 @@
|
||||
%YAML 1.1
|
||||
%TAG !u! tag:unity3d.com,2011:
|
||||
--- !u!114 &11400000
|
||||
MonoBehaviour:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 0}
|
||||
m_Enabled: 1
|
||||
m_EditorHideFlags: 0
|
||||
m_Script: {fileID: 11500000, guid: 5834b5087d578d24c926ce20cd31e6d6, type: 3}
|
||||
m_Name: Localization-Assets-Japanese (Japan) (ja-JP)_ContentUpdateGroupSchema
|
||||
m_EditorClassIdentifier:
|
||||
m_Group: {fileID: 11400000, guid: 2b05cac5050c285498c6d11b749f865a, type: 2}
|
||||
m_StaticContent: 0
|
||||
-8
@@ -1,8 +0,0 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 2a43b55ded538f14f92548425cfb42e5
|
||||
NativeFormatImporter:
|
||||
externalObjects: {}
|
||||
mainObjectFileID: 11400000
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
-45
@@ -1,45 +0,0 @@
|
||||
%YAML 1.1
|
||||
%TAG !u! tag:unity3d.com,2011:
|
||||
--- !u!114 &11400000
|
||||
MonoBehaviour:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 0}
|
||||
m_Enabled: 1
|
||||
m_EditorHideFlags: 0
|
||||
m_Script: {fileID: 11500000, guid: e5d17a21594effb4e9591490b009e7aa, type: 3}
|
||||
m_Name: Shared_En_BundledAssetGroupSchema
|
||||
m_EditorClassIdentifier:
|
||||
m_Group: {fileID: 11400000, guid: b05cfdc6bbe312248b8be84a1ff01e32, type: 2}
|
||||
m_InternalBundleIdMode: 1
|
||||
m_Compression: 1
|
||||
m_IncludeAddressInCatalog: 1
|
||||
m_IncludeGUIDInCatalog: 1
|
||||
m_IncludeLabelsInCatalog: 1
|
||||
m_InternalIdNamingMode: 0
|
||||
m_CacheClearBehavior: 0
|
||||
m_IncludeInBuild: 1
|
||||
m_BundledAssetProviderType:
|
||||
m_AssemblyName: Unity.ResourceManager, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null
|
||||
m_ClassName: UnityEngine.ResourceManagement.ResourceProviders.BundledAssetProvider
|
||||
m_ForceUniqueProvider: 0
|
||||
m_UseAssetBundleCache: 1
|
||||
m_UseAssetBundleCrc: 1
|
||||
m_UseAssetBundleCrcForCachedBundles: 1
|
||||
m_UseUWRForLocalBundles: 0
|
||||
m_Timeout: 0
|
||||
m_ChunkedTransfer: 0
|
||||
m_RedirectLimit: -1
|
||||
m_RetryCount: 0
|
||||
m_BuildPath:
|
||||
m_Id: 2ae6a7549097a9b4caefe44b61b1fcc5
|
||||
m_LoadPath:
|
||||
m_Id: 631ec5c677ab8084fb9be9b9ee39c6ef
|
||||
m_BundleMode: 0
|
||||
m_AssetBundleProviderType:
|
||||
m_AssemblyName: Unity.ResourceManager, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null
|
||||
m_ClassName: UnityEngine.ResourceManagement.ResourceProviders.AssetBundleProvider
|
||||
m_BundleNaming: 0
|
||||
m_AssetLoadMode: 0
|
||||
-8
@@ -1,8 +0,0 @@
|
||||
fileFormatVersion: 2
|
||||
guid: d477ca02df8260c4ba977ebfe3a551fb
|
||||
NativeFormatImporter:
|
||||
externalObjects: {}
|
||||
mainObjectFileID: 11400000
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
-16
@@ -1,16 +0,0 @@
|
||||
%YAML 1.1
|
||||
%TAG !u! tag:unity3d.com,2011:
|
||||
--- !u!114 &11400000
|
||||
MonoBehaviour:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 0}
|
||||
m_Enabled: 1
|
||||
m_EditorHideFlags: 0
|
||||
m_Script: {fileID: 11500000, guid: 5834b5087d578d24c926ce20cd31e6d6, type: 3}
|
||||
m_Name: Shared_En_ContentUpdateGroupSchema
|
||||
m_EditorClassIdentifier:
|
||||
m_Group: {fileID: 11400000, guid: b05cfdc6bbe312248b8be84a1ff01e32, type: 2}
|
||||
m_StaticContent: 0
|
||||
-8
@@ -1,8 +0,0 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 7f3b2267eaa2569429428b05d408314e
|
||||
NativeFormatImporter:
|
||||
externalObjects: {}
|
||||
mainObjectFileID: 11400000
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
-45
@@ -1,45 +0,0 @@
|
||||
%YAML 1.1
|
||||
%TAG !u! tag:unity3d.com,2011:
|
||||
--- !u!114 &11400000
|
||||
MonoBehaviour:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 0}
|
||||
m_Enabled: 1
|
||||
m_EditorHideFlags: 0
|
||||
m_Script: {fileID: 11500000, guid: e5d17a21594effb4e9591490b009e7aa, type: 3}
|
||||
m_Name: Shared_Es_BundledAssetGroupSchema
|
||||
m_EditorClassIdentifier:
|
||||
m_Group: {fileID: 11400000, guid: 0418ce3bee30b98409aa4d268d7d89d7, type: 2}
|
||||
m_InternalBundleIdMode: 1
|
||||
m_Compression: 1
|
||||
m_IncludeAddressInCatalog: 1
|
||||
m_IncludeGUIDInCatalog: 1
|
||||
m_IncludeLabelsInCatalog: 1
|
||||
m_InternalIdNamingMode: 0
|
||||
m_CacheClearBehavior: 0
|
||||
m_IncludeInBuild: 1
|
||||
m_BundledAssetProviderType:
|
||||
m_AssemblyName: Unity.ResourceManager, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null
|
||||
m_ClassName: UnityEngine.ResourceManagement.ResourceProviders.BundledAssetProvider
|
||||
m_ForceUniqueProvider: 0
|
||||
m_UseAssetBundleCache: 1
|
||||
m_UseAssetBundleCrc: 1
|
||||
m_UseAssetBundleCrcForCachedBundles: 1
|
||||
m_UseUWRForLocalBundles: 0
|
||||
m_Timeout: 0
|
||||
m_ChunkedTransfer: 0
|
||||
m_RedirectLimit: -1
|
||||
m_RetryCount: 0
|
||||
m_BuildPath:
|
||||
m_Id: 2ae6a7549097a9b4caefe44b61b1fcc5
|
||||
m_LoadPath:
|
||||
m_Id: 631ec5c677ab8084fb9be9b9ee39c6ef
|
||||
m_BundleMode: 0
|
||||
m_AssetBundleProviderType:
|
||||
m_AssemblyName: Unity.ResourceManager, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null
|
||||
m_ClassName: UnityEngine.ResourceManagement.ResourceProviders.AssetBundleProvider
|
||||
m_BundleNaming: 0
|
||||
m_AssetLoadMode: 0
|
||||
-8
@@ -1,8 +0,0 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 196a7a55916e25f4896599ddb2660f7a
|
||||
NativeFormatImporter:
|
||||
externalObjects: {}
|
||||
mainObjectFileID: 11400000
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
-16
@@ -1,16 +0,0 @@
|
||||
%YAML 1.1
|
||||
%TAG !u! tag:unity3d.com,2011:
|
||||
--- !u!114 &11400000
|
||||
MonoBehaviour:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 0}
|
||||
m_Enabled: 1
|
||||
m_EditorHideFlags: 0
|
||||
m_Script: {fileID: 11500000, guid: 5834b5087d578d24c926ce20cd31e6d6, type: 3}
|
||||
m_Name: Shared_Es_ContentUpdateGroupSchema
|
||||
m_EditorClassIdentifier:
|
||||
m_Group: {fileID: 11400000, guid: 0418ce3bee30b98409aa4d268d7d89d7, type: 2}
|
||||
m_StaticContent: 0
|
||||
-8
@@ -1,8 +0,0 @@
|
||||
fileFormatVersion: 2
|
||||
guid: a40977b297f51304cb63f1a2787ea4a4
|
||||
NativeFormatImporter:
|
||||
externalObjects: {}
|
||||
mainObjectFileID: 11400000
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
-45
@@ -1,45 +0,0 @@
|
||||
%YAML 1.1
|
||||
%TAG !u! tag:unity3d.com,2011:
|
||||
--- !u!114 &11400000
|
||||
MonoBehaviour:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 0}
|
||||
m_Enabled: 1
|
||||
m_EditorHideFlags: 0
|
||||
m_Script: {fileID: 11500000, guid: e5d17a21594effb4e9591490b009e7aa, type: 3}
|
||||
m_Name: Shared_Ja_BundledAssetGroupSchema
|
||||
m_EditorClassIdentifier:
|
||||
m_Group: {fileID: 11400000, guid: 6e0c8dfd5e5e1c74b80cecc21df2d9cd, type: 2}
|
||||
m_InternalBundleIdMode: 1
|
||||
m_Compression: 1
|
||||
m_IncludeAddressInCatalog: 1
|
||||
m_IncludeGUIDInCatalog: 1
|
||||
m_IncludeLabelsInCatalog: 1
|
||||
m_InternalIdNamingMode: 0
|
||||
m_CacheClearBehavior: 0
|
||||
m_IncludeInBuild: 1
|
||||
m_BundledAssetProviderType:
|
||||
m_AssemblyName: Unity.ResourceManager, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null
|
||||
m_ClassName: UnityEngine.ResourceManagement.ResourceProviders.BundledAssetProvider
|
||||
m_ForceUniqueProvider: 0
|
||||
m_UseAssetBundleCache: 1
|
||||
m_UseAssetBundleCrc: 1
|
||||
m_UseAssetBundleCrcForCachedBundles: 1
|
||||
m_UseUWRForLocalBundles: 0
|
||||
m_Timeout: 0
|
||||
m_ChunkedTransfer: 0
|
||||
m_RedirectLimit: -1
|
||||
m_RetryCount: 0
|
||||
m_BuildPath:
|
||||
m_Id: 2ae6a7549097a9b4caefe44b61b1fcc5
|
||||
m_LoadPath:
|
||||
m_Id: 631ec5c677ab8084fb9be9b9ee39c6ef
|
||||
m_BundleMode: 0
|
||||
m_AssetBundleProviderType:
|
||||
m_AssemblyName: Unity.ResourceManager, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null
|
||||
m_ClassName: UnityEngine.ResourceManagement.ResourceProviders.AssetBundleProvider
|
||||
m_BundleNaming: 0
|
||||
m_AssetLoadMode: 0
|
||||
-8
@@ -1,8 +0,0 @@
|
||||
fileFormatVersion: 2
|
||||
guid: a2d74ab6ace13ab4ba87121fa11aea2c
|
||||
NativeFormatImporter:
|
||||
externalObjects: {}
|
||||
mainObjectFileID: 11400000
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
-16
@@ -1,16 +0,0 @@
|
||||
%YAML 1.1
|
||||
%TAG !u! tag:unity3d.com,2011:
|
||||
--- !u!114 &11400000
|
||||
MonoBehaviour:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 0}
|
||||
m_Enabled: 1
|
||||
m_EditorHideFlags: 0
|
||||
m_Script: {fileID: 11500000, guid: 5834b5087d578d24c926ce20cd31e6d6, type: 3}
|
||||
m_Name: Shared_Ja_ContentUpdateGroupSchema
|
||||
m_EditorClassIdentifier:
|
||||
m_Group: {fileID: 11400000, guid: 6e0c8dfd5e5e1c74b80cecc21df2d9cd, type: 2}
|
||||
m_StaticContent: 0
|
||||
-8
@@ -1,8 +0,0 @@
|
||||
fileFormatVersion: 2
|
||||
guid: b4ec695ab7e7a7848a6e4df4156f5091
|
||||
NativeFormatImporter:
|
||||
externalObjects: {}
|
||||
mainObjectFileID: 11400000
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
-45
@@ -1,45 +0,0 @@
|
||||
%YAML 1.1
|
||||
%TAG !u! tag:unity3d.com,2011:
|
||||
--- !u!114 &11400000
|
||||
MonoBehaviour:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 0}
|
||||
m_Enabled: 1
|
||||
m_EditorHideFlags: 0
|
||||
m_Script: {fileID: 11500000, guid: e5d17a21594effb4e9591490b009e7aa, type: 3}
|
||||
m_Name: Shared_Pt_BundledAssetGroupSchema
|
||||
m_EditorClassIdentifier:
|
||||
m_Group: {fileID: 11400000, guid: f0205c268777acb459dddbb4690d7c27, type: 2}
|
||||
m_InternalBundleIdMode: 1
|
||||
m_Compression: 1
|
||||
m_IncludeAddressInCatalog: 1
|
||||
m_IncludeGUIDInCatalog: 1
|
||||
m_IncludeLabelsInCatalog: 1
|
||||
m_InternalIdNamingMode: 0
|
||||
m_CacheClearBehavior: 0
|
||||
m_IncludeInBuild: 1
|
||||
m_BundledAssetProviderType:
|
||||
m_AssemblyName: Unity.ResourceManager, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null
|
||||
m_ClassName: UnityEngine.ResourceManagement.ResourceProviders.BundledAssetProvider
|
||||
m_ForceUniqueProvider: 0
|
||||
m_UseAssetBundleCache: 1
|
||||
m_UseAssetBundleCrc: 1
|
||||
m_UseAssetBundleCrcForCachedBundles: 1
|
||||
m_UseUWRForLocalBundles: 0
|
||||
m_Timeout: 0
|
||||
m_ChunkedTransfer: 0
|
||||
m_RedirectLimit: -1
|
||||
m_RetryCount: 0
|
||||
m_BuildPath:
|
||||
m_Id: 2ae6a7549097a9b4caefe44b61b1fcc5
|
||||
m_LoadPath:
|
||||
m_Id: 631ec5c677ab8084fb9be9b9ee39c6ef
|
||||
m_BundleMode: 0
|
||||
m_AssetBundleProviderType:
|
||||
m_AssemblyName: Unity.ResourceManager, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null
|
||||
m_ClassName: UnityEngine.ResourceManagement.ResourceProviders.AssetBundleProvider
|
||||
m_BundleNaming: 0
|
||||
m_AssetLoadMode: 0
|
||||
-8
@@ -1,8 +0,0 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 7219f667c0a45a642a5b943eb602dffc
|
||||
NativeFormatImporter:
|
||||
externalObjects: {}
|
||||
mainObjectFileID: 11400000
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
-16
@@ -1,16 +0,0 @@
|
||||
%YAML 1.1
|
||||
%TAG !u! tag:unity3d.com,2011:
|
||||
--- !u!114 &11400000
|
||||
MonoBehaviour:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 0}
|
||||
m_Enabled: 1
|
||||
m_EditorHideFlags: 0
|
||||
m_Script: {fileID: 11500000, guid: 5834b5087d578d24c926ce20cd31e6d6, type: 3}
|
||||
m_Name: Shared_Pt_ContentUpdateGroupSchema
|
||||
m_EditorClassIdentifier:
|
||||
m_Group: {fileID: 11400000, guid: f0205c268777acb459dddbb4690d7c27, type: 2}
|
||||
m_StaticContent: 0
|
||||
-8
@@ -1,8 +0,0 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 6f490fe007fca8342aceea06f8b0e054
|
||||
NativeFormatImporter:
|
||||
externalObjects: {}
|
||||
mainObjectFileID: 11400000
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
-45
@@ -1,45 +0,0 @@
|
||||
%YAML 1.1
|
||||
%TAG !u! tag:unity3d.com,2011:
|
||||
--- !u!114 &11400000
|
||||
MonoBehaviour:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 0}
|
||||
m_Enabled: 1
|
||||
m_EditorHideFlags: 0
|
||||
m_Script: {fileID: 11500000, guid: e5d17a21594effb4e9591490b009e7aa, type: 3}
|
||||
m_Name: Shared_Ru_BundledAssetGroupSchema
|
||||
m_EditorClassIdentifier:
|
||||
m_Group: {fileID: 11400000, guid: d029169cdb68ba848a0f0260aeddae55, type: 2}
|
||||
m_InternalBundleIdMode: 1
|
||||
m_Compression: 1
|
||||
m_IncludeAddressInCatalog: 1
|
||||
m_IncludeGUIDInCatalog: 1
|
||||
m_IncludeLabelsInCatalog: 1
|
||||
m_InternalIdNamingMode: 0
|
||||
m_CacheClearBehavior: 0
|
||||
m_IncludeInBuild: 1
|
||||
m_BundledAssetProviderType:
|
||||
m_AssemblyName: Unity.ResourceManager, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null
|
||||
m_ClassName: UnityEngine.ResourceManagement.ResourceProviders.BundledAssetProvider
|
||||
m_ForceUniqueProvider: 0
|
||||
m_UseAssetBundleCache: 1
|
||||
m_UseAssetBundleCrc: 1
|
||||
m_UseAssetBundleCrcForCachedBundles: 1
|
||||
m_UseUWRForLocalBundles: 0
|
||||
m_Timeout: 0
|
||||
m_ChunkedTransfer: 0
|
||||
m_RedirectLimit: -1
|
||||
m_RetryCount: 0
|
||||
m_BuildPath:
|
||||
m_Id: 2ae6a7549097a9b4caefe44b61b1fcc5
|
||||
m_LoadPath:
|
||||
m_Id: 631ec5c677ab8084fb9be9b9ee39c6ef
|
||||
m_BundleMode: 0
|
||||
m_AssetBundleProviderType:
|
||||
m_AssemblyName: Unity.ResourceManager, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null
|
||||
m_ClassName: UnityEngine.ResourceManagement.ResourceProviders.AssetBundleProvider
|
||||
m_BundleNaming: 0
|
||||
m_AssetLoadMode: 0
|
||||
-8
@@ -1,8 +0,0 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 955397ef95649794a87b3c52b818f5c1
|
||||
NativeFormatImporter:
|
||||
externalObjects: {}
|
||||
mainObjectFileID: 11400000
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
-16
@@ -1,16 +0,0 @@
|
||||
%YAML 1.1
|
||||
%TAG !u! tag:unity3d.com,2011:
|
||||
--- !u!114 &11400000
|
||||
MonoBehaviour:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 0}
|
||||
m_Enabled: 1
|
||||
m_EditorHideFlags: 0
|
||||
m_Script: {fileID: 11500000, guid: 5834b5087d578d24c926ce20cd31e6d6, type: 3}
|
||||
m_Name: Shared_Ru_ContentUpdateGroupSchema
|
||||
m_EditorClassIdentifier:
|
||||
m_Group: {fileID: 11400000, guid: d029169cdb68ba848a0f0260aeddae55, type: 2}
|
||||
m_StaticContent: 0
|
||||
-8
@@ -1,8 +0,0 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 5012207ff0e21364d9462964eb6374ef
|
||||
NativeFormatImporter:
|
||||
externalObjects: {}
|
||||
mainObjectFileID: 11400000
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
-45
@@ -1,45 +0,0 @@
|
||||
%YAML 1.1
|
||||
%TAG !u! tag:unity3d.com,2011:
|
||||
--- !u!114 &11400000
|
||||
MonoBehaviour:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 0}
|
||||
m_Enabled: 1
|
||||
m_EditorHideFlags: 0
|
||||
m_Script: {fileID: 11500000, guid: e5d17a21594effb4e9591490b009e7aa, type: 3}
|
||||
m_Name: Shared_Typography_BundledAssetGroupSchema
|
||||
m_EditorClassIdentifier:
|
||||
m_Group: {fileID: 11400000, guid: 1051221ba75fcea47b67648acedc5383, type: 2}
|
||||
m_InternalBundleIdMode: 1
|
||||
m_Compression: 1
|
||||
m_IncludeAddressInCatalog: 1
|
||||
m_IncludeGUIDInCatalog: 1
|
||||
m_IncludeLabelsInCatalog: 1
|
||||
m_InternalIdNamingMode: 0
|
||||
m_CacheClearBehavior: 0
|
||||
m_IncludeInBuild: 1
|
||||
m_BundledAssetProviderType:
|
||||
m_AssemblyName: Unity.ResourceManager, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null
|
||||
m_ClassName: UnityEngine.ResourceManagement.ResourceProviders.BundledAssetProvider
|
||||
m_ForceUniqueProvider: 0
|
||||
m_UseAssetBundleCache: 1
|
||||
m_UseAssetBundleCrc: 1
|
||||
m_UseAssetBundleCrcForCachedBundles: 1
|
||||
m_UseUWRForLocalBundles: 0
|
||||
m_Timeout: 0
|
||||
m_ChunkedTransfer: 0
|
||||
m_RedirectLimit: -1
|
||||
m_RetryCount: 0
|
||||
m_BuildPath:
|
||||
m_Id: 2ae6a7549097a9b4caefe44b61b1fcc5
|
||||
m_LoadPath:
|
||||
m_Id: 631ec5c677ab8084fb9be9b9ee39c6ef
|
||||
m_BundleMode: 0
|
||||
m_AssetBundleProviderType:
|
||||
m_AssemblyName: Unity.ResourceManager, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null
|
||||
m_ClassName: UnityEngine.ResourceManagement.ResourceProviders.AssetBundleProvider
|
||||
m_BundleNaming: 0
|
||||
m_AssetLoadMode: 0
|
||||
-8
@@ -1,8 +0,0 @@
|
||||
fileFormatVersion: 2
|
||||
guid: b39cbcc4de75ad6468842c36c7e5138d
|
||||
NativeFormatImporter:
|
||||
externalObjects: {}
|
||||
mainObjectFileID: 11400000
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
-16
@@ -1,16 +0,0 @@
|
||||
%YAML 1.1
|
||||
%TAG !u! tag:unity3d.com,2011:
|
||||
--- !u!114 &11400000
|
||||
MonoBehaviour:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 0}
|
||||
m_Enabled: 1
|
||||
m_EditorHideFlags: 0
|
||||
m_Script: {fileID: 11500000, guid: 5834b5087d578d24c926ce20cd31e6d6, type: 3}
|
||||
m_Name: Shared_Typography_ContentUpdateGroupSchema
|
||||
m_EditorClassIdentifier:
|
||||
m_Group: {fileID: 11400000, guid: 1051221ba75fcea47b67648acedc5383, type: 2}
|
||||
m_StaticContent: 0
|
||||
-8
@@ -1,8 +0,0 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 6bd7e9bcac1e24042b79a54f47f52106
|
||||
NativeFormatImporter:
|
||||
externalObjects: {}
|
||||
mainObjectFileID: 11400000
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -1,30 +0,0 @@
|
||||
%YAML 1.1
|
||||
%TAG !u! tag:unity3d.com,2011:
|
||||
--- !u!114 &11400000
|
||||
MonoBehaviour:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 0}
|
||||
m_Enabled: 1
|
||||
m_EditorHideFlags: 0
|
||||
m_Script: {fileID: 11500000, guid: bbb281ee3bf0b054c82ac2347e9e782c, type: 3}
|
||||
m_Name: Shared_En
|
||||
m_EditorClassIdentifier:
|
||||
m_GroupName: Shared_En
|
||||
m_Data:
|
||||
m_SerializedData: []
|
||||
m_GUID: 29507884f3280da4dafc16e8e5f4b2bb
|
||||
m_SerializeEntries:
|
||||
- m_GUID: 35bfbd48717731341844dee0f2cf0e25
|
||||
m_Address: Sprite/Obj_en
|
||||
m_ReadOnly: 0
|
||||
m_SerializedLabels: []
|
||||
FlaggedDuringContentUpdateRestriction: 0
|
||||
m_ReadOnly: 0
|
||||
m_Settings: {fileID: 11400000, guid: 77169ce22e430f64fb36c771815a4a7b, type: 2}
|
||||
m_SchemaSet:
|
||||
m_Schemas:
|
||||
- {fileID: 11400000, guid: d477ca02df8260c4ba977ebfe3a551fb, type: 2}
|
||||
- {fileID: 11400000, guid: 7f3b2267eaa2569429428b05d408314e, type: 2}
|
||||
@@ -1,8 +0,0 @@
|
||||
fileFormatVersion: 2
|
||||
guid: b05cfdc6bbe312248b8be84a1ff01e32
|
||||
NativeFormatImporter:
|
||||
externalObjects: {}
|
||||
mainObjectFileID: 11400000
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -1,30 +0,0 @@
|
||||
%YAML 1.1
|
||||
%TAG !u! tag:unity3d.com,2011:
|
||||
--- !u!114 &11400000
|
||||
MonoBehaviour:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 0}
|
||||
m_Enabled: 1
|
||||
m_EditorHideFlags: 0
|
||||
m_Script: {fileID: 11500000, guid: bbb281ee3bf0b054c82ac2347e9e782c, type: 3}
|
||||
m_Name: Shared_Es
|
||||
m_EditorClassIdentifier:
|
||||
m_GroupName: Shared_Es
|
||||
m_Data:
|
||||
m_SerializedData: []
|
||||
m_GUID: 829895de7eaa2a34183c7ef64d0dce86
|
||||
m_SerializeEntries:
|
||||
- m_GUID: e3eb91f33227ac24a91c0f7bedb3cade
|
||||
m_Address: Sprite/Obj_es
|
||||
m_ReadOnly: 0
|
||||
m_SerializedLabels: []
|
||||
FlaggedDuringContentUpdateRestriction: 0
|
||||
m_ReadOnly: 0
|
||||
m_Settings: {fileID: 11400000, guid: 77169ce22e430f64fb36c771815a4a7b, type: 2}
|
||||
m_SchemaSet:
|
||||
m_Schemas:
|
||||
- {fileID: 11400000, guid: 196a7a55916e25f4896599ddb2660f7a, type: 2}
|
||||
- {fileID: 11400000, guid: a40977b297f51304cb63f1a2787ea4a4, type: 2}
|
||||
@@ -1,8 +0,0 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 0418ce3bee30b98409aa4d268d7d89d7
|
||||
NativeFormatImporter:
|
||||
externalObjects: {}
|
||||
mainObjectFileID: 11400000
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -1,30 +0,0 @@
|
||||
%YAML 1.1
|
||||
%TAG !u! tag:unity3d.com,2011:
|
||||
--- !u!114 &11400000
|
||||
MonoBehaviour:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 0}
|
||||
m_Enabled: 1
|
||||
m_EditorHideFlags: 0
|
||||
m_Script: {fileID: 11500000, guid: bbb281ee3bf0b054c82ac2347e9e782c, type: 3}
|
||||
m_Name: Shared_Ja
|
||||
m_EditorClassIdentifier:
|
||||
m_GroupName: Shared_Ja
|
||||
m_Data:
|
||||
m_SerializedData: []
|
||||
m_GUID: 3c94b26b5824a4d4994401b840a0eb17
|
||||
m_SerializeEntries:
|
||||
- m_GUID: 44a964dd5827e044387ce89002c789ed
|
||||
m_Address: Sprite/Obj_ja-JP
|
||||
m_ReadOnly: 0
|
||||
m_SerializedLabels: []
|
||||
FlaggedDuringContentUpdateRestriction: 0
|
||||
m_ReadOnly: 0
|
||||
m_Settings: {fileID: 11400000, guid: 77169ce22e430f64fb36c771815a4a7b, type: 2}
|
||||
m_SchemaSet:
|
||||
m_Schemas:
|
||||
- {fileID: 11400000, guid: a2d74ab6ace13ab4ba87121fa11aea2c, type: 2}
|
||||
- {fileID: 11400000, guid: b4ec695ab7e7a7848a6e4df4156f5091, type: 2}
|
||||
@@ -1,8 +0,0 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 6e0c8dfd5e5e1c74b80cecc21df2d9cd
|
||||
NativeFormatImporter:
|
||||
externalObjects: {}
|
||||
mainObjectFileID: 11400000
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -1,30 +0,0 @@
|
||||
%YAML 1.1
|
||||
%TAG !u! tag:unity3d.com,2011:
|
||||
--- !u!114 &11400000
|
||||
MonoBehaviour:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 0}
|
||||
m_Enabled: 1
|
||||
m_EditorHideFlags: 0
|
||||
m_Script: {fileID: 11500000, guid: bbb281ee3bf0b054c82ac2347e9e782c, type: 3}
|
||||
m_Name: Shared_Pt
|
||||
m_EditorClassIdentifier:
|
||||
m_GroupName: Shared_Pt
|
||||
m_Data:
|
||||
m_SerializedData: []
|
||||
m_GUID: 93a7fef7294edab48960d5cfe05bc589
|
||||
m_SerializeEntries:
|
||||
- m_GUID: 7b906b9ce46cacd4d9937d1dc7af0048
|
||||
m_Address: Sprite/Obj_pt-BR
|
||||
m_ReadOnly: 0
|
||||
m_SerializedLabels: []
|
||||
FlaggedDuringContentUpdateRestriction: 0
|
||||
m_ReadOnly: 0
|
||||
m_Settings: {fileID: 11400000, guid: 77169ce22e430f64fb36c771815a4a7b, type: 2}
|
||||
m_SchemaSet:
|
||||
m_Schemas:
|
||||
- {fileID: 11400000, guid: 7219f667c0a45a642a5b943eb602dffc, type: 2}
|
||||
- {fileID: 11400000, guid: 6f490fe007fca8342aceea06f8b0e054, type: 2}
|
||||
@@ -1,8 +0,0 @@
|
||||
fileFormatVersion: 2
|
||||
guid: f0205c268777acb459dddbb4690d7c27
|
||||
NativeFormatImporter:
|
||||
externalObjects: {}
|
||||
mainObjectFileID: 11400000
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -1,30 +0,0 @@
|
||||
%YAML 1.1
|
||||
%TAG !u! tag:unity3d.com,2011:
|
||||
--- !u!114 &11400000
|
||||
MonoBehaviour:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 0}
|
||||
m_Enabled: 1
|
||||
m_EditorHideFlags: 0
|
||||
m_Script: {fileID: 11500000, guid: bbb281ee3bf0b054c82ac2347e9e782c, type: 3}
|
||||
m_Name: Shared_Ru
|
||||
m_EditorClassIdentifier:
|
||||
m_GroupName: Shared_Ru
|
||||
m_Data:
|
||||
m_SerializedData: []
|
||||
m_GUID: bc556d622a58aee4ea7684147eb2e432
|
||||
m_SerializeEntries:
|
||||
- m_GUID: b636657f158aa9d4a85197ee4e1e072c
|
||||
m_Address: Sprite/Obj_ru
|
||||
m_ReadOnly: 0
|
||||
m_SerializedLabels: []
|
||||
FlaggedDuringContentUpdateRestriction: 0
|
||||
m_ReadOnly: 0
|
||||
m_Settings: {fileID: 11400000, guid: 77169ce22e430f64fb36c771815a4a7b, type: 2}
|
||||
m_SchemaSet:
|
||||
m_Schemas:
|
||||
- {fileID: 11400000, guid: 955397ef95649794a87b3c52b818f5c1, type: 2}
|
||||
- {fileID: 11400000, guid: 5012207ff0e21364d9462964eb6374ef, type: 2}
|
||||
@@ -1,8 +0,0 @@
|
||||
fileFormatVersion: 2
|
||||
guid: d029169cdb68ba848a0f0260aeddae55
|
||||
NativeFormatImporter:
|
||||
externalObjects: {}
|
||||
mainObjectFileID: 11400000
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -1,49 +0,0 @@
|
||||
%YAML 1.1
|
||||
%TAG !u! tag:unity3d.com,2011:
|
||||
--- !u!114 &11400000
|
||||
MonoBehaviour:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 0}
|
||||
m_Enabled: 1
|
||||
m_EditorHideFlags: 0
|
||||
m_Script: {fileID: 11500000, guid: bbb281ee3bf0b054c82ac2347e9e782c, type: 3}
|
||||
m_Name: Shared_Typography
|
||||
m_EditorClassIdentifier:
|
||||
m_GroupName: Shared_Typography
|
||||
m_Data:
|
||||
m_SerializedData: []
|
||||
m_GUID: 7663edc0b4aef67468c672b641025c85
|
||||
m_SerializeEntries:
|
||||
- m_GUID: e41dce1e1d6e0564b95054b5c8b8bb4c
|
||||
m_Address: Typography/Profile/ZhHans
|
||||
m_ReadOnly: 0
|
||||
m_SerializedLabels:
|
||||
- Typography
|
||||
FlaggedDuringContentUpdateRestriction: 0
|
||||
- m_GUID: 67cd590df91846b4d81698d68426e616
|
||||
m_Address: Typography/Profile/Japanese
|
||||
m_ReadOnly: 0
|
||||
m_SerializedLabels:
|
||||
- Typography
|
||||
FlaggedDuringContentUpdateRestriction: 0
|
||||
- m_GUID: d53382c947b5edc4a88216c5fcf37b38
|
||||
m_Address: Typography/Profile/Latin
|
||||
m_ReadOnly: 0
|
||||
m_SerializedLabels:
|
||||
- Typography
|
||||
FlaggedDuringContentUpdateRestriction: 0
|
||||
- m_GUID: f82150d4b378dc441884eb47d4f67b7c
|
||||
m_Address: Typography/Profile/Russian
|
||||
m_ReadOnly: 0
|
||||
m_SerializedLabels:
|
||||
- Typography
|
||||
FlaggedDuringContentUpdateRestriction: 0
|
||||
m_ReadOnly: 0
|
||||
m_Settings: {fileID: 11400000, guid: 77169ce22e430f64fb36c771815a4a7b, type: 2}
|
||||
m_SchemaSet:
|
||||
m_Schemas:
|
||||
- {fileID: 11400000, guid: b39cbcc4de75ad6468842c36c7e5138d, type: 2}
|
||||
- {fileID: 11400000, guid: 6bd7e9bcac1e24042b79a54f47f52106, type: 2}
|
||||
@@ -1,8 +0,0 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 1051221ba75fcea47b67648acedc5383
|
||||
NativeFormatImporter:
|
||||
externalObjects: {}
|
||||
mainObjectFileID: 11400000
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -1,5 +1,5 @@
|
||||
fileFormatVersion: 2
|
||||
guid: d3a91a90ebc2fd742bd10a66893940b8
|
||||
guid: 638cd3fa1c2470d42aceb69214c01786
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
@@ -0,0 +1,260 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using AibisDream.Kit;
|
||||
using AibisDream.SaveSystem;
|
||||
using FMOD;
|
||||
using FMOD.Studio;
|
||||
using Newtonsoft.Json;
|
||||
using Newtonsoft.Json.Linq;
|
||||
using NUnit.Framework;
|
||||
|
||||
namespace AibisDream.SystemEditor.Tests
|
||||
{
|
||||
public sealed class AudioGlobalParameterSnapshotTests
|
||||
{
|
||||
[Test]
|
||||
public void SetFromYarn_TracksOnlySuccessfulUniqueNames()
|
||||
{
|
||||
var api = new FakeFmodGlobalParameterApi();
|
||||
api.SetByNameResults["missing"] = RESULT.ERR_EVENT_NOTFOUND;
|
||||
var registry = new YarnGlobalParameterRegistry(api);
|
||||
|
||||
Assert.That(registry.SetFromYarn("reverb", 0.4f), Is.EqualTo(RESULT.OK));
|
||||
Assert.That(registry.SetFromYarn("REVERB", 0.7f), Is.EqualTo(RESULT.OK));
|
||||
Assert.That(
|
||||
registry.SetFromYarn("missing", 1f),
|
||||
Is.EqualTo(RESULT.ERR_EVENT_NOTFOUND));
|
||||
|
||||
Assert.That(registry.TrackedNames, Has.Count.EqualTo(1));
|
||||
Assert.That(registry.TrackedNames.Single(), Is.EqualTo("reverb"));
|
||||
Assert.That(api.SetByNameCalls[0].IgnoreSeekSpeed, Is.False);
|
||||
Assert.That(api.SetByNameCalls[1].IgnoreSeekSpeed, Is.False);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void CaptureFinalValues_ReadsFmodFinalValueAndRetainsFailuresForRetry()
|
||||
{
|
||||
var api = new FakeFmodGlobalParameterApi();
|
||||
api.Reads["stage"] = new ParameterRead(9f, 2.37f, RESULT.OK);
|
||||
api.Reads["missing"] = new ParameterRead(0f, 0f, RESULT.ERR_EVENT_NOTFOUND);
|
||||
var registry = new YarnGlobalParameterRegistry(api);
|
||||
registry.SetFromYarn("stage", 9f);
|
||||
registry.SetFromYarn("missing", 1f);
|
||||
var warnings = new List<string>();
|
||||
|
||||
var snapshot = registry.CaptureFinalValues(warnings.Add);
|
||||
|
||||
Assert.That(snapshot, Has.Count.EqualTo(1));
|
||||
Assert.That(snapshot["stage"], Is.EqualTo(2.37f));
|
||||
Assert.That(snapshot["stage"], Is.Not.EqualTo(9f));
|
||||
Assert.That(registry.TrackedNames, Is.EquivalentTo(new[] { "stage", "missing" }));
|
||||
Assert.That(warnings, Has.Count.EqualTo(1));
|
||||
StringAssert.Contains("missing", warnings[0]);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void ResetToDefaults_RemovesOnlyParametersThatFmodResetSuccessfully()
|
||||
{
|
||||
var goodId = new PARAMETER_ID { data1 = 1, data2 = 2 };
|
||||
var failedId = new PARAMETER_ID { data1 = 3, data2 = 4 };
|
||||
var api = new FakeFmodGlobalParameterApi();
|
||||
api.Descriptions["good"] = new ParameterDescriptionRead(
|
||||
new PARAMETER_DESCRIPTION { id = goodId, defaultvalue = 0.25f },
|
||||
RESULT.OK);
|
||||
api.Descriptions["failed"] = new ParameterDescriptionRead(
|
||||
new PARAMETER_DESCRIPTION { id = failedId, defaultvalue = 0.75f },
|
||||
RESULT.OK);
|
||||
api.SetByIdResults[failedId] = RESULT.ERR_INVALID_PARAM;
|
||||
var registry = new YarnGlobalParameterRegistry(api);
|
||||
registry.SetFromYarn("good", 1f);
|
||||
registry.SetFromYarn("failed", 1f);
|
||||
var warnings = new List<string>();
|
||||
|
||||
registry.ResetToDefaults(warnings.Add);
|
||||
|
||||
Assert.That(registry.TrackedNames, Is.EquivalentTo(new[] { "failed" }));
|
||||
Assert.That(api.SetByIdCalls, Has.Count.EqualTo(2));
|
||||
var goodReset = api.SetByIdCalls.Single(call => call.Id.Equals(goodId));
|
||||
Assert.That(goodReset.Value, Is.EqualTo(0.25f));
|
||||
Assert.That(goodReset.IgnoreSeekSpeed, Is.True);
|
||||
Assert.That(warnings, Has.Count.EqualTo(1));
|
||||
StringAssert.Contains("failed", warnings[0]);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void RestoreFinalValues_JumpsImmediatelyThenNextYarnTargetUsesSeekSpeed()
|
||||
{
|
||||
var api = new FakeFmodGlobalParameterApi();
|
||||
api.SetByNameResults["missing"] = RESULT.ERR_EVENT_NOTFOUND;
|
||||
var registry = new YarnGlobalParameterRegistry(api);
|
||||
var warnings = new List<string>();
|
||||
|
||||
registry.RestoreFinalValues(
|
||||
new Dictionary<string, float>
|
||||
{
|
||||
["stage"] = 2.37f,
|
||||
["missing"] = 1f
|
||||
},
|
||||
warnings.Add);
|
||||
registry.SetFromYarn("stage", 3f);
|
||||
|
||||
Assert.That(api.SetByNameCalls, Has.Count.EqualTo(3));
|
||||
Assert.That(api.SetByNameCalls[0].Value, Is.EqualTo(2.37f));
|
||||
Assert.That(api.SetByNameCalls[0].IgnoreSeekSpeed, Is.True);
|
||||
Assert.That(api.SetByNameCalls[1].Name, Is.EqualTo("missing"));
|
||||
Assert.That(api.SetByNameCalls[1].IgnoreSeekSpeed, Is.True);
|
||||
Assert.That(api.SetByNameCalls[2].Value, Is.EqualTo(3f));
|
||||
Assert.That(api.SetByNameCalls[2].IgnoreSeekSpeed, Is.False);
|
||||
Assert.That(registry.TrackedNames, Is.EquivalentTo(new[] { "stage" }));
|
||||
Assert.That(warnings, Has.Count.EqualTo(1));
|
||||
StringAssert.Contains("missing", warnings[0]);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void AudioSnapshotDto_RoundTripsFinalValuesWithoutChangingSchema()
|
||||
{
|
||||
var source = new SaveSnapshot();
|
||||
source.sections[SnapshotProviderIds.Audio] = new AudioSnapshotDto
|
||||
{
|
||||
ambState = "Dream",
|
||||
yarnGlobalParameters = new Dictionary<string, float>
|
||||
{
|
||||
["reverb"] = 0.42f,
|
||||
["hs1LogStage"] = 2.37f
|
||||
}
|
||||
};
|
||||
|
||||
var json = JsonConvert.SerializeObject(source);
|
||||
var restored = JsonConvert.DeserializeObject<SaveSnapshot>(json);
|
||||
var audio = ((JObject)restored.sections[SnapshotProviderIds.Audio])
|
||||
.ToObject<AudioSnapshotDto>();
|
||||
|
||||
Assert.That(restored.schemaVersion, Is.EqualTo(2));
|
||||
Assert.That(SaveSnapshotSchema.CurrentVersion, Is.EqualTo(2));
|
||||
Assert.That(audio.yarnGlobalParameters["reverb"], Is.EqualTo(0.42f));
|
||||
Assert.That(audio.yarnGlobalParameters["hs1LogStage"], Is.EqualTo(2.37f));
|
||||
}
|
||||
|
||||
private readonly struct SetByNameCall
|
||||
{
|
||||
internal SetByNameCall(string name, float value, bool ignoreSeekSpeed)
|
||||
{
|
||||
Name = name;
|
||||
Value = value;
|
||||
IgnoreSeekSpeed = ignoreSeekSpeed;
|
||||
}
|
||||
|
||||
internal string Name { get; }
|
||||
internal float Value { get; }
|
||||
internal bool IgnoreSeekSpeed { get; }
|
||||
}
|
||||
|
||||
private readonly struct SetByIdCall
|
||||
{
|
||||
internal SetByIdCall(PARAMETER_ID id, float value, bool ignoreSeekSpeed)
|
||||
{
|
||||
Id = id;
|
||||
Value = value;
|
||||
IgnoreSeekSpeed = ignoreSeekSpeed;
|
||||
}
|
||||
|
||||
internal PARAMETER_ID Id { get; }
|
||||
internal float Value { get; }
|
||||
internal bool IgnoreSeekSpeed { get; }
|
||||
}
|
||||
|
||||
private readonly struct ParameterRead
|
||||
{
|
||||
internal ParameterRead(float value, float finalValue, RESULT result)
|
||||
{
|
||||
Value = value;
|
||||
FinalValue = finalValue;
|
||||
Result = result;
|
||||
}
|
||||
|
||||
internal float Value { get; }
|
||||
internal float FinalValue { get; }
|
||||
internal RESULT Result { get; }
|
||||
}
|
||||
|
||||
private readonly struct ParameterDescriptionRead
|
||||
{
|
||||
internal ParameterDescriptionRead(
|
||||
PARAMETER_DESCRIPTION description,
|
||||
RESULT result)
|
||||
{
|
||||
Description = description;
|
||||
Result = result;
|
||||
}
|
||||
|
||||
internal PARAMETER_DESCRIPTION Description { get; }
|
||||
internal RESULT Result { get; }
|
||||
}
|
||||
|
||||
private sealed class FakeFmodGlobalParameterApi : IFmodGlobalParameterApi
|
||||
{
|
||||
internal readonly Dictionary<string, RESULT> SetByNameResults =
|
||||
new(System.StringComparer.OrdinalIgnoreCase);
|
||||
internal readonly Dictionary<string, ParameterRead> Reads =
|
||||
new(System.StringComparer.OrdinalIgnoreCase);
|
||||
internal readonly Dictionary<string, ParameterDescriptionRead> Descriptions =
|
||||
new(System.StringComparer.OrdinalIgnoreCase);
|
||||
internal readonly Dictionary<PARAMETER_ID, RESULT> SetByIdResults = new();
|
||||
internal readonly List<SetByNameCall> SetByNameCalls = new();
|
||||
internal readonly List<SetByIdCall> SetByIdCalls = new();
|
||||
|
||||
public RESULT SetParameterByName(
|
||||
string name,
|
||||
float value,
|
||||
bool ignoreSeekSpeed)
|
||||
{
|
||||
SetByNameCalls.Add(new SetByNameCall(name, value, ignoreSeekSpeed));
|
||||
return SetByNameResults.TryGetValue(name, out var result)
|
||||
? result
|
||||
: RESULT.OK;
|
||||
}
|
||||
|
||||
public RESULT GetParameterByName(
|
||||
string name,
|
||||
out float value,
|
||||
out float finalValue)
|
||||
{
|
||||
if (Reads.TryGetValue(name, out var read))
|
||||
{
|
||||
value = read.Value;
|
||||
finalValue = read.FinalValue;
|
||||
return read.Result;
|
||||
}
|
||||
|
||||
value = 0f;
|
||||
finalValue = 0f;
|
||||
return RESULT.OK;
|
||||
}
|
||||
|
||||
public RESULT GetParameterDescriptionByName(
|
||||
string name,
|
||||
out PARAMETER_DESCRIPTION description)
|
||||
{
|
||||
if (Descriptions.TryGetValue(name, out var read))
|
||||
{
|
||||
description = read.Description;
|
||||
return read.Result;
|
||||
}
|
||||
|
||||
description = default;
|
||||
return RESULT.ERR_EVENT_NOTFOUND;
|
||||
}
|
||||
|
||||
public RESULT SetParameterById(
|
||||
PARAMETER_ID id,
|
||||
float value,
|
||||
bool ignoreSeekSpeed)
|
||||
{
|
||||
SetByIdCalls.Add(new SetByIdCall(id, value, ignoreSeekSpeed));
|
||||
return SetByIdResults.TryGetValue(id, out var result)
|
||||
? result
|
||||
: RESULT.OK;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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<MarkupAttribute>())
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
+1
-1
@@ -1,5 +1,5 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 72a6797e130a7f643bd42bf648543ec2
|
||||
guid: d476e493adf4855468ce46409dae6661
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
@@ -1,5 +1,5 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 1ccca9c8cbbc67e47be376cc9db7ce55
|
||||
guid: 665561f3805a96446b45ea1d50bfe520
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
@@ -0,0 +1,117 @@
|
||||
#if UNITY_EDITOR
|
||||
using System.Linq;
|
||||
using AibisDream.Kit;
|
||||
using AibisDream.UI;
|
||||
using AibisDream.Utility;
|
||||
using NUnit.Framework;
|
||||
using UnityEditor;
|
||||
using UnityEngine;
|
||||
|
||||
namespace AibisDream.DeveloperMode.Editor
|
||||
{
|
||||
public sealed class DeveloperModeCoreTests
|
||||
{
|
||||
[Test]
|
||||
public void RuntimeLogBufferEvictsOldestAndPreservesOrder()
|
||||
{
|
||||
var buffer = new RuntimeLogBuffer(3);
|
||||
buffer.Add(LogEntry.System(LogLevel.Info, LogCategory.General, "one"));
|
||||
buffer.Add(LogEntry.System(LogLevel.Warning, LogCategory.Save, "two"));
|
||||
buffer.Add(LogEntry.System(LogLevel.Error, LogCategory.Yarn, "three"));
|
||||
buffer.Add(LogEntry.System(LogLevel.Fatal, LogCategory.Scene, "four"));
|
||||
|
||||
var snapshot = buffer.Snapshot();
|
||||
Assert.That(snapshot.Select(item => item.Message), Is.EqualTo(new[] { "two", "three", "four" }));
|
||||
Assert.That(snapshot.Select(item => item.Sequence), Is.Ordered.Ascending);
|
||||
Assert.That(snapshot.Length, Is.EqualTo(buffer.Capacity));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void RuntimeLogBufferAcceptsConcurrentWritersAndClears()
|
||||
{
|
||||
var buffer = new RuntimeLogBuffer(128);
|
||||
System.Threading.Tasks.Parallel.For(0, 1000, index =>
|
||||
buffer.Add(LogEntry.System(LogLevel.Info, LogCategory.General, index.ToString())));
|
||||
|
||||
var snapshot = buffer.Snapshot();
|
||||
Assert.That(snapshot.Length, Is.EqualTo(128));
|
||||
Assert.That(snapshot.Select(item => item.Sequence), Is.Ordered.Ascending);
|
||||
buffer.Clear();
|
||||
Assert.That(buffer.Snapshot(), Is.Empty);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void YarnStorageTypedSettersAndDebugSnapshotAreReadOnlyAndConsistent()
|
||||
{
|
||||
var go = new GameObject("YarnVariableStorage Test");
|
||||
try
|
||||
{
|
||||
var storage = go.AddComponent<YarnVariableStorage>();
|
||||
storage.SetValue("$score", 12.5f);
|
||||
storage.SetValue("$global_flag", true);
|
||||
storage.SetValue("$name", "AIBIS");
|
||||
|
||||
var snapshot = DeveloperVariableSnapshot.Capture(storage, null);
|
||||
Assert.That(snapshot.Select(item => item.Name),
|
||||
Is.EquivalentTo(new[] { "$score", "$global_flag", "$name" }));
|
||||
Assert.That(snapshot.Single(item => item.Name == "$score").Value, Is.EqualTo("12.5"));
|
||||
Assert.That(snapshot.Single(item => item.Name == "$global_flag").IsGlobal, Is.True);
|
||||
Assert.That(snapshot.All(item => item.HasRuntimeOverride), Is.True);
|
||||
|
||||
storage.ClearLocal();
|
||||
var remaining = DeveloperVariableSnapshot.Capture(storage, null);
|
||||
Assert.That(remaining.Select(item => item.Name), Is.EqualTo(new[] { "$global_flag" }));
|
||||
storage.Clear();
|
||||
Assert.That(DeveloperVariableSnapshot.Capture(storage, null), Is.Empty);
|
||||
}
|
||||
finally
|
||||
{
|
||||
Object.DestroyImmediate(go);
|
||||
}
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void EditorBuildEnablesDeveloperMode()
|
||||
{
|
||||
Assert.That(DeveloperModeGate.IsEnabled, Is.True);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void PrefabIsConfiguredAndPersistenceReferencesIt()
|
||||
{
|
||||
const string prefabPath = "Assets/GameContent/Feature_MainUI/Prefabs/DeveloperModePanel.prefab";
|
||||
const string scenePath = "Assets/Scenes/Persistence.unity";
|
||||
var prefab = AssetDatabase.LoadAssetAtPath<GameObject>(prefabPath);
|
||||
Assert.That(prefab, Is.Not.Null);
|
||||
Assert.That(prefab.activeSelf, Is.False);
|
||||
var panel = prefab.GetComponent<DeveloperModePanel>();
|
||||
Assert.That(panel, Is.Not.Null);
|
||||
Assert.That(panel.IsConfigured, Is.True);
|
||||
Assert.That(prefab.GetComponent<RectTransform>().sizeDelta, Is.EqualTo(new Vector2(840f, 760f)));
|
||||
Assert.That(AssetDatabase.GetDependencies(scenePath), Does.Contain(prefabPath));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void PanelToggleIsNonModalAndEscapeClosesIt()
|
||||
{
|
||||
const string prefabPath = "Assets/GameContent/Feature_MainUI/Prefabs/DeveloperModePanel.prefab";
|
||||
var prefab = AssetDatabase.LoadAssetAtPath<GameObject>(prefabPath);
|
||||
var instance = Object.Instantiate(prefab);
|
||||
try
|
||||
{
|
||||
var panel = instance.GetComponent<DeveloperModePanel>();
|
||||
var originalScale = Time.timeScale;
|
||||
panel.TogglePanel();
|
||||
Assert.That(panel.IsOpen, Is.True);
|
||||
Assert.That(Time.timeScale, Is.EqualTo(originalScale));
|
||||
Assert.That(panel.HandleEscape(), Is.True);
|
||||
Assert.That(panel.IsOpen, Is.False);
|
||||
}
|
||||
finally
|
||||
{
|
||||
Object.DestroyImmediate(instance);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
+1
-1
@@ -1,5 +1,5 @@
|
||||
fileFormatVersion: 2
|
||||
guid: a84a91ac052fc3444aa281c7d6a4f29e
|
||||
guid: b95ba9d1e80e3dc4e83bc408569c4955
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
@@ -0,0 +1,476 @@
|
||||
#if UNITY_EDITOR
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using AibisDream.Framework;
|
||||
using AibisDream.SaveSystem;
|
||||
using Newtonsoft.Json;
|
||||
using NUnit.Framework;
|
||||
using UnityEngine;
|
||||
|
||||
namespace AibisDream.DeveloperMode.Editor.Tests
|
||||
{
|
||||
public sealed class TestSaveSystemTests
|
||||
{
|
||||
private string _root;
|
||||
private TestSaveRepository _repository;
|
||||
|
||||
[SetUp]
|
||||
public void SetUp()
|
||||
{
|
||||
_root = Path.Combine(
|
||||
Path.GetTempPath(),
|
||||
"AibisDream-TestSaveTests",
|
||||
Guid.NewGuid().ToString("N"));
|
||||
_repository = new TestSaveRepository(_root);
|
||||
}
|
||||
|
||||
[TearDown]
|
||||
public void TearDown()
|
||||
{
|
||||
if (Directory.Exists(_root))
|
||||
{
|
||||
Directory.Delete(_root, true);
|
||||
}
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void Record_SameAnchor_ReplacesSnapshotAndKeepsFirstSeenOrder()
|
||||
{
|
||||
var first = _repository.Record(CreateRequest("ChapterA", "ProjectA", "NodeA", "SceneOne"));
|
||||
var second = _repository.Record(CreateRequest("ChapterA", "ProjectA", "NodeA", "SceneTwo"));
|
||||
var scan = _repository.Scan("1.0");
|
||||
|
||||
Assert.That(scan.Entries.Count, Is.EqualTo(1));
|
||||
Assert.That(second.Meta.firstSeenOrder, Is.EqualTo(first.Meta.firstSeenOrder));
|
||||
Assert.That(scan.Entries[0].Meta.sceneName, Is.EqualTo("SceneTwo"));
|
||||
Assert.That(_repository.TryLoad(scan.Entries[0], out var snapshot, out var error), Is.True, error);
|
||||
Assert.That(snapshot.scene.sceneName, Is.EqualTo("SceneTwo"));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void Record_ReplacingEarlierEntry_DoesNotMoveItsOrder()
|
||||
{
|
||||
_repository.Record(CreateRequest("ChapterA", "ProjectA", "NodeA", "SceneA"));
|
||||
_repository.Record(CreateRequest("ChapterA", "ProjectA", "NodeB", "SceneB"));
|
||||
_repository.Record(CreateRequest("ChapterA", "ProjectA", "NodeA", "SceneA2"));
|
||||
|
||||
var entries = _repository.Scan().Entries.ToArray();
|
||||
Assert.That(entries.Select(item => item.NodeName), Is.EqualTo(new[] { "NodeA", "NodeB" }));
|
||||
Assert.That(entries[0].Meta.firstSeenOrder, Is.LessThan(entries[1].Meta.firstSeenOrder));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void Scan_RetainsMissingSnapshotAsInvalid()
|
||||
{
|
||||
var entry = _repository.Record(CreateRequest("ChapterA", "ProjectA", "NodeA", "SceneA"));
|
||||
File.Delete(entry.SnapshotPath);
|
||||
|
||||
var scan = _repository.Scan();
|
||||
|
||||
Assert.That(scan.ValidCount, Is.Zero);
|
||||
Assert.That(scan.InvalidCount, Is.EqualTo(1));
|
||||
Assert.That(scan.Entries[0].Status, Is.EqualTo(TestSaveEntryStatus.MissingSnapshot));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void Scan_RetainsOrphanSnapshotWithBrokenMeta()
|
||||
{
|
||||
var directory = Path.Combine(_root, "chapter", "broken");
|
||||
Directory.CreateDirectory(directory);
|
||||
File.WriteAllText(
|
||||
Path.Combine(directory, TestSaveRepository.SnapshotFileName),
|
||||
"{}",
|
||||
Encoding.UTF8);
|
||||
|
||||
var scan = _repository.Scan();
|
||||
|
||||
Assert.That(scan.InvalidCount, Is.EqualTo(1));
|
||||
Assert.That(scan.Entries[0].Status, Is.EqualTo(TestSaveEntryStatus.CorruptMeta));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void Validate_CorruptSnapshot_IsRetainedAndDisabledOnNextScan()
|
||||
{
|
||||
var entry = _repository.Record(CreateRequest("ChapterA", "ProjectA", "NodeA", "SceneA"));
|
||||
File.WriteAllText(entry.SnapshotPath, "{broken", Encoding.UTF8);
|
||||
|
||||
Assert.That(_repository.Validate(entry, out var error), Is.False);
|
||||
var rescanned = _repository.Scan();
|
||||
|
||||
Assert.That(error, Does.Contain("无法解析"));
|
||||
Assert.That(rescanned.InvalidCount, Is.EqualTo(1));
|
||||
Assert.That(rescanned.Entries[0].Status, Is.EqualTo(TestSaveEntryStatus.CorruptSnapshot));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void Scan_RecoversCompleteStagingDirectory()
|
||||
{
|
||||
var final = Path.Combine(_root, "chapter", "node");
|
||||
var staging = final + ".__staging";
|
||||
Directory.CreateDirectory(staging);
|
||||
var request = CreateRequest("ChapterA", "ProjectA", "NodeA", "SceneA");
|
||||
var meta = CreateMeta(request, 1);
|
||||
File.WriteAllText(
|
||||
Path.Combine(staging, TestSaveRepository.MetaFileName),
|
||||
JsonConvert.SerializeObject(meta),
|
||||
Encoding.UTF8);
|
||||
File.WriteAllText(
|
||||
Path.Combine(staging, TestSaveRepository.SnapshotFileName),
|
||||
SnapshotPersistence.Serialize(request.Snapshot),
|
||||
Encoding.UTF8);
|
||||
|
||||
var scan = _repository.Scan();
|
||||
|
||||
Assert.That(Directory.Exists(final), Is.True);
|
||||
Assert.That(Directory.Exists(staging), Is.False);
|
||||
Assert.That(scan.ValidCount, Is.EqualTo(1));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void Record_ConcurrentRequests_ProducesCompleteUniqueEntries()
|
||||
{
|
||||
var tasks = Enumerable.Range(0, 12)
|
||||
.Select(index => Task.Run(() =>
|
||||
_repository.Record(CreateRequest(
|
||||
"ChapterA",
|
||||
"ProjectA",
|
||||
$"Node{index:00}",
|
||||
$"Scene{index:00}"))))
|
||||
.ToArray();
|
||||
|
||||
Task.WaitAll(tasks);
|
||||
var scan = _repository.Scan();
|
||||
|
||||
Assert.That(scan.ValidCount, Is.EqualTo(12));
|
||||
Assert.That(scan.InvalidCount, Is.Zero);
|
||||
Assert.That(scan.Entries.Select(item => item.Meta.firstSeenOrder).Distinct().Count(), Is.EqualTo(12));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void DeleteInvalid_RemovesOnlyInvalidEntries()
|
||||
{
|
||||
_repository.Record(CreateRequest("ChapterA", "ProjectA", "NodeA", "SceneA"));
|
||||
var invalid = _repository.Record(CreateRequest("ChapterA", "ProjectA", "NodeB", "SceneB"));
|
||||
File.Delete(invalid.SnapshotPath);
|
||||
|
||||
var removed = _repository.DeleteInvalid(out var error);
|
||||
var scan = _repository.Scan();
|
||||
|
||||
Assert.That(error, Is.Null);
|
||||
Assert.That(removed, Is.EqualTo(1));
|
||||
Assert.That(scan.ValidCount, Is.EqualTo(1));
|
||||
Assert.That(scan.InvalidCount, Is.Zero);
|
||||
}
|
||||
|
||||
[TestCase(new[] { "hub" }, true)]
|
||||
[TestCase(new[] { "linear" }, true)]
|
||||
[TestCase(new[] { "content" }, true)]
|
||||
[TestCase(new[] { "event" }, false)]
|
||||
[TestCase(new[] { "hub", "no_save" }, false)]
|
||||
[TestCase(new string[0], true)]
|
||||
public void SilentCoverageEvaluator_MatchesSavePointTagSemantics(string[] tags, bool expected)
|
||||
{
|
||||
var actual = SavePointEvaluator.EvaluateNodeTagsForAutoSaveSilently(
|
||||
"Node",
|
||||
tags,
|
||||
out _);
|
||||
Assert.That(actual, Is.EqualTo(expected));
|
||||
}
|
||||
|
||||
[TestCase(new[] { "content" }, NodeSaveTiming.NodeEnter, SaveResumeMode.RestartNode)]
|
||||
[TestCase(new[] { "interaction" }, NodeSaveTiming.DialogueExit, SaveResumeMode.StateOnly)]
|
||||
[TestCase(new[] { "content", "save_on_exit" }, NodeSaveTiming.DialogueExit, SaveResumeMode.StateOnly)]
|
||||
[TestCase(new[] { "event", "save_on_exit" }, NodeSaveTiming.DialogueExit, SaveResumeMode.StateOnly)]
|
||||
[TestCase(new[] { "interaction", "save_on_exit" }, NodeSaveTiming.DialogueExit, SaveResumeMode.StateOnly)]
|
||||
[TestCase(new[] { "interaction", "no_save" }, NodeSaveTiming.None, SaveResumeMode.RestartNode)]
|
||||
[TestCase(new[] { "content", "event" }, NodeSaveTiming.None, SaveResumeMode.RestartNode)]
|
||||
public void ResolveNodePolicy_MapsNodeTypeAndModifiers(
|
||||
string[] tags,
|
||||
NodeSaveTiming expectedTiming,
|
||||
SaveResumeMode expectedResumeMode)
|
||||
{
|
||||
var policy = SavePointEvaluator.ResolveNodePolicy(
|
||||
"Node",
|
||||
tags,
|
||||
out _,
|
||||
logWarnings: false);
|
||||
|
||||
Assert.That(policy.Timing, Is.EqualTo(expectedTiming));
|
||||
Assert.That(policy.ResumeMode, Is.EqualTo(expectedResumeMode));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void Repository_StateOnlySnapshot_PreservesAnchorNode()
|
||||
{
|
||||
var request = CreateRequest("ChapterA", "ProjectA", "InteractionNode", "SceneA");
|
||||
request.Snapshot.anchor.startDialogueOnRestore = false;
|
||||
request.SaveTrigger = "DialogueExit";
|
||||
request.ResumeMode = nameof(SaveResumeMode.StateOnly);
|
||||
request.StartDialogueOnRestore = false;
|
||||
request.DedupeKey = TestSaveRecorder.BuildDedupeKey(
|
||||
request.SceneSoName,
|
||||
request.YarnProjectId,
|
||||
request.NodeName,
|
||||
request.ResumeMode);
|
||||
request.EntryId = TestSaveRepository.StableHash(request.DedupeKey);
|
||||
|
||||
var entry = _repository.Record(request);
|
||||
var loaded = _repository.TryLoad(entry, out var snapshot, out var error);
|
||||
|
||||
Assert.That(loaded, Is.True, error);
|
||||
Assert.That(entry.NodeName, Is.EqualTo("InteractionNode"));
|
||||
Assert.That(entry.ResumeMode, Is.EqualTo(nameof(SaveResumeMode.StateOnly)));
|
||||
Assert.That(snapshot.anchor.nodeName, Is.EqualTo("InteractionNode"));
|
||||
Assert.That(snapshot.anchor.startDialogueOnRestore, Is.False);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void SnapshotPersistence_MissingRestoreFlag_IsRejected()
|
||||
{
|
||||
const string json =
|
||||
"{\"schemaVersion\":2,\"anchor\":{\"sceneSoName\":\"ChapterA\"," +
|
||||
"\"yarnProjectId\":\"ProjectA\",\"nodeName\":\"NodeA\"}}";
|
||||
|
||||
Assert.Throws<Newtonsoft.Json.JsonSerializationException>(
|
||||
() => SnapshotPersistence.Deserialize(json));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void SerialTaskQueue_DoesNotDropOrReorderRequests()
|
||||
{
|
||||
var queue = new SerialTaskQueue();
|
||||
var releaseFirst = new TaskCompletionSource<bool>();
|
||||
var firstStarted = new TaskCompletionSource<bool>();
|
||||
var order = new List<int>();
|
||||
|
||||
var first = queue.Enqueue(async () =>
|
||||
{
|
||||
order.Add(1);
|
||||
firstStarted.SetResult(true);
|
||||
await releaseFirst.Task;
|
||||
order.Add(2);
|
||||
});
|
||||
var second = queue.Enqueue(() =>
|
||||
{
|
||||
order.Add(3);
|
||||
return Task.CompletedTask;
|
||||
});
|
||||
|
||||
Assert.That(firstStarted.Task.Wait(TimeSpan.FromSeconds(2)), Is.True);
|
||||
Assert.That(order, Is.EqualTo(new[] { 1 }));
|
||||
Assert.That(queue.PendingCount, Is.EqualTo(2));
|
||||
|
||||
releaseFirst.SetResult(true);
|
||||
Assert.That(
|
||||
Task.WaitAll(
|
||||
new[] { first.Completion, second.Completion },
|
||||
TimeSpan.FromSeconds(2)),
|
||||
Is.True);
|
||||
|
||||
Assert.That(first.Sequence, Is.LessThan(second.Sequence));
|
||||
Assert.That(order, Is.EqualTo(new[] { 1, 2, 3 }));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void SaveRequest_MapsResumeModeToExplicitAnchorFlag()
|
||||
{
|
||||
var enter = SaveRequest.NodeEnter("NodeA", "ProjectA", "ChapterA", 3, 7);
|
||||
var exit = SaveRequest.DialogueExit("NodeB", "ProjectA", "ChapterA", 3, 9);
|
||||
|
||||
Assert.That(enter.AnchorSpec.NodeName, Is.EqualTo("NodeA"));
|
||||
Assert.That(enter.AnchorSpec.StartDialogueOnRestore, Is.True);
|
||||
Assert.That(exit.AnchorSpec.NodeName, Is.EqualTo("NodeB"));
|
||||
Assert.That(exit.AnchorSpec.StartDialogueOnRestore, Is.False);
|
||||
}
|
||||
|
||||
[TestCase(false, "DetourNode", true)]
|
||||
[TestCase(false, "NoSaveNode", false)]
|
||||
[TestCase(true, "AnotherNode", true)]
|
||||
public void ValidateDialogueTiming_NonExitRequestIgnoresLaterDialogueState(
|
||||
bool explicitCommand,
|
||||
string currentNodeName,
|
||||
bool isDialogueRunning)
|
||||
{
|
||||
var request = explicitCommand
|
||||
? SaveRequest.Explicit("SourceNode", "ProjectA", "ChapterA", 3, 7)
|
||||
: SaveRequest.NodeEnter("SourceNode", "ProjectA", "ChapterA", 3, 7);
|
||||
|
||||
var valid = SaveRestoreOrchestrator.ValidateDialogueTiming(
|
||||
request,
|
||||
currentDialogueRunId: 99,
|
||||
currentFlowRevision: 101,
|
||||
currentNodeName: currentNodeName,
|
||||
isDialogueRunning: isDialogueRunning,
|
||||
out var reason);
|
||||
|
||||
Assert.That(valid, Is.True, reason);
|
||||
Assert.That(request.AnchorSpec.NodeName, Is.EqualTo("SourceNode"));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void ValidateDialogueTiming_ConsecutiveNodeEnterRequestsRemainEligible()
|
||||
{
|
||||
var first = SaveRequest.NodeEnter("FirstNode", "ProjectA", "ChapterA", 3, 7);
|
||||
var second = SaveRequest.NodeEnter("SecondNode", "ProjectA", "ChapterA", 3, 8);
|
||||
|
||||
var firstValid = SaveRestoreOrchestrator.ValidateDialogueTiming(
|
||||
first,
|
||||
currentDialogueRunId: 3,
|
||||
currentFlowRevision: 8,
|
||||
currentNodeName: "SecondNode",
|
||||
isDialogueRunning: true,
|
||||
out var firstReason);
|
||||
var secondValid = SaveRestoreOrchestrator.ValidateDialogueTiming(
|
||||
second,
|
||||
currentDialogueRunId: 3,
|
||||
currentFlowRevision: 8,
|
||||
currentNodeName: "SecondNode",
|
||||
isDialogueRunning: true,
|
||||
out var secondReason);
|
||||
|
||||
Assert.That(firstValid, Is.True, firstReason);
|
||||
Assert.That(secondValid, Is.True, secondReason);
|
||||
Assert.That(first.AnchorSpec.NodeName, Is.EqualTo("FirstNode"));
|
||||
Assert.That(second.AnchorSpec.NodeName, Is.EqualTo("SecondNode"));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void ValidateDialogueTiming_DialogueExitAcceptsStableCompletedFlow()
|
||||
{
|
||||
var request = SaveRequest.DialogueExit(
|
||||
"InteractionNode",
|
||||
"ProjectA",
|
||||
"ChapterA",
|
||||
3,
|
||||
9);
|
||||
|
||||
var valid = SaveRestoreOrchestrator.ValidateDialogueTiming(
|
||||
request,
|
||||
currentDialogueRunId: 3,
|
||||
currentFlowRevision: 9,
|
||||
currentNodeName: null,
|
||||
isDialogueRunning: false,
|
||||
out var reason);
|
||||
|
||||
Assert.That(valid, Is.True, reason);
|
||||
}
|
||||
|
||||
[TestCase(4, 9, null, false, "dialogue run changed")]
|
||||
[TestCase(3, 10, null, false, "flow revision changed")]
|
||||
[TestCase(3, 9, "DetourNode", false, "current Yarn node")]
|
||||
[TestCase(3, 9, "NoSaveNode", false, "current Yarn node")]
|
||||
[TestCase(3, 9, "JumpTarget", true, "still running")]
|
||||
public void ValidateDialogueTiming_DialogueExitRejectsChangedOrRunningFlow(
|
||||
long currentDialogueRunId,
|
||||
long currentFlowRevision,
|
||||
string currentNodeName,
|
||||
bool isDialogueRunning,
|
||||
string expectedReason)
|
||||
{
|
||||
var request = SaveRequest.DialogueExit(
|
||||
"InteractionNode",
|
||||
"ProjectA",
|
||||
"ChapterA",
|
||||
3,
|
||||
9);
|
||||
|
||||
var valid = SaveRestoreOrchestrator.ValidateDialogueTiming(
|
||||
request,
|
||||
currentDialogueRunId,
|
||||
currentFlowRevision,
|
||||
currentNodeName,
|
||||
isDialogueRunning,
|
||||
out var reason);
|
||||
|
||||
Assert.That(valid, Is.False);
|
||||
Assert.That(reason, Does.Contain(expectedReason));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void InteractionLock_ComposesWithDialogueLock()
|
||||
{
|
||||
var gameObject = new GameObject("EventSystemEx-Test");
|
||||
try
|
||||
{
|
||||
var eventSystem = gameObject.AddComponent<EventSystemEx>();
|
||||
EnumEventSystem.Global.Send(InteractionEventEnum.DialogStart);
|
||||
var checkpointLock = eventSystem.AcquireInteractionLock("test");
|
||||
|
||||
EnumEventSystem.Global.Send(InteractionEventEnum.DialogEnd);
|
||||
Assert.That(eventSystem.isLocked, Is.True);
|
||||
|
||||
checkpointLock.Dispose();
|
||||
Assert.That(eventSystem.isLocked, Is.False);
|
||||
}
|
||||
finally
|
||||
{
|
||||
UnityEngine.Object.DestroyImmediate(gameObject);
|
||||
}
|
||||
}
|
||||
|
||||
private static TestSaveRecordRequest CreateRequest(
|
||||
string sceneSoName,
|
||||
string yarnProject,
|
||||
string nodeName,
|
||||
string sceneName)
|
||||
{
|
||||
var key = TestSaveRecorder.BuildDedupeKey(sceneSoName, yarnProject, nodeName);
|
||||
return new TestSaveRecordRequest
|
||||
{
|
||||
Snapshot = new SaveSnapshot
|
||||
{
|
||||
gameVersion = "1.0",
|
||||
savedAt = "2026-01-01 00:00:00",
|
||||
scene = new SceneSnapshotDto { sceneName = sceneName },
|
||||
anchor = new AnchorSnapshot
|
||||
{
|
||||
sceneSoName = sceneSoName,
|
||||
yarnProjectId = yarnProject,
|
||||
nodeName = nodeName,
|
||||
startDialogueOnRestore = true
|
||||
}
|
||||
},
|
||||
DedupeKey = key,
|
||||
EntryId = TestSaveRepository.StableHash(key),
|
||||
ChapterId = sceneSoName,
|
||||
ChapterTitle = sceneSoName,
|
||||
SceneSoName = sceneSoName,
|
||||
YarnProjectId = yarnProject,
|
||||
NodeName = nodeName,
|
||||
SaveTrigger = "NodeEnter",
|
||||
ResumeMode = nameof(SaveResumeMode.RestartNode),
|
||||
StartDialogueOnRestore = true,
|
||||
SceneName = sceneName,
|
||||
GameVersion = "1.0"
|
||||
};
|
||||
}
|
||||
|
||||
private static TestSaveMeta CreateMeta(TestSaveRecordRequest request, long order)
|
||||
{
|
||||
return new TestSaveMeta
|
||||
{
|
||||
entryId = request.EntryId,
|
||||
dedupeKey = request.DedupeKey,
|
||||
chapterId = request.ChapterId,
|
||||
chapterTitle = request.ChapterTitle,
|
||||
sceneSoName = request.SceneSoName,
|
||||
yarnProjectId = request.YarnProjectId,
|
||||
nodeName = request.NodeName,
|
||||
saveTrigger = request.SaveTrigger,
|
||||
resumeMode = request.ResumeMode,
|
||||
startDialogueOnRestore = request.StartDialogueOnRestore,
|
||||
sceneName = request.SceneName,
|
||||
firstSeenOrder = order,
|
||||
firstRecordedAt = "2026-01-01 00:00:00",
|
||||
lastRecordedAt = "2026-01-01 00:00:00",
|
||||
snapshotSchemaVersion = SaveSnapshotSchema.CurrentVersion,
|
||||
gameVersion = request.GameVersion
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
+1
-1
@@ -1,5 +1,5 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 895c6fa68b9b4ed4286362a258247f4a
|
||||
guid: cbbba0c3621cf204483f453bad2d75a4
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
@@ -1803,14 +1803,6 @@ namespace AibisDream.FrameAnimation.Editor
|
||||
AddReadOnly(body, "ID", flow.Id);
|
||||
AddReadOnly(body, "Entry Node", flow.EntryNodeId);
|
||||
AddBoundProperty(body, serialized, property.FindPropertyRelative("displayName"), "Display Name");
|
||||
AddBoundProperty(
|
||||
body,
|
||||
serialized,
|
||||
property.FindPropertyRelative("asyncCompletionMode"),
|
||||
"Async Completion");
|
||||
body.Add(CreateCallout(
|
||||
"Only affects async playback when the resolved terminal clip loops.",
|
||||
"fa-callout--info"));
|
||||
AddBoundProperty(body, serialized, property.FindPropertyRelative("hasEndBehaviorOverride"), "Override End Behavior");
|
||||
AddBoundProperty(body, serialized, property.FindPropertyRelative("endBehaviorOverride"), "End Behavior");
|
||||
|
||||
@@ -2521,14 +2513,6 @@ namespace AibisDream.FrameAnimation.Editor
|
||||
EditorGUILayout.PropertyField(property.FindPropertyRelative("entryNodeId"));
|
||||
}
|
||||
EditorGUILayout.PropertyField(property.FindPropertyRelative("displayName"));
|
||||
EditorGUILayout.PropertyField(
|
||||
property.FindPropertyRelative("asyncCompletionMode"),
|
||||
new GUIContent(
|
||||
"Async Completion",
|
||||
"Only affects async playback when the resolved terminal clip loops."));
|
||||
EditorGUILayout.HelpBox(
|
||||
"Async Completion only affects a Flow whose resolved terminal clip loops.",
|
||||
MessageType.Info);
|
||||
EditorGUILayout.PropertyField(property.FindPropertyRelative("hasEndBehaviorOverride"));
|
||||
if (property.FindPropertyRelative("hasEndBehaviorOverride").boolValue)
|
||||
{
|
||||
|
||||
@@ -0,0 +1,77 @@
|
||||
using AibisDream.FrameAnimation;
|
||||
using AibisDream.SaveSystem;
|
||||
using AibisDream.Utility;
|
||||
using NUnit.Framework;
|
||||
using UnityEditor;
|
||||
using UnityEditor.AddressableAssets;
|
||||
using UnityEngine;
|
||||
|
||||
namespace AibisDream.SystemEditor.Tests
|
||||
{
|
||||
public sealed class FrameAnimationActorIntegrationTests
|
||||
{
|
||||
private const string PrefabPath =
|
||||
"Assets/Prefabs/FixSystemPrefabs/FrameAnimationActor.prefab";
|
||||
|
||||
[Test]
|
||||
public void ActorType_AppendsFrameAnimationWithoutChangingExistingValues()
|
||||
{
|
||||
Assert.That((int)ActorType.Sprite, Is.EqualTo(0));
|
||||
Assert.That((int)ActorType.Anima, Is.EqualTo(1));
|
||||
Assert.That((int)ActorType.AnimaEx, Is.EqualTo(2));
|
||||
Assert.That((int)ActorType.FrameAnimation, Is.EqualTo(3));
|
||||
Assert.That(
|
||||
System.Enum.TryParse("FrameAnimation", out ActorType parsed),
|
||||
Is.True);
|
||||
Assert.That(parsed, Is.EqualTo(ActorType.FrameAnimation));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void Prefab_HasRequiredComponentsAndPersistentAddress()
|
||||
{
|
||||
var prefab = AssetDatabase.LoadAssetAtPath<GameObject>(PrefabPath);
|
||||
Assert.That(prefab, Is.Not.Null);
|
||||
Assert.That(prefab.GetComponent<SpriteRenderer>(), Is.Not.Null);
|
||||
Assert.That(prefab.GetComponent<FrameAnimationPlayer>(), Is.Not.Null);
|
||||
Assert.That(prefab.GetComponent<FrameAnimationActor>(), Is.Not.Null);
|
||||
Assert.That(prefab.GetComponent<FrameAnimationPlayer>().PlayOnEnable, Is.False);
|
||||
Assert.That(prefab.GetComponent<FrameAnimationPlayer>().Graph, Is.Null);
|
||||
|
||||
var guid = AssetDatabase.AssetPathToGUID(PrefabPath);
|
||||
var entry = AddressableAssetSettingsDefaultObject.Settings.FindAssetEntry(guid);
|
||||
Assert.That(entry, Is.Not.Null);
|
||||
Assert.That(entry.address, Is.EqualTo(ConstRef.FrameAnimationActorPrefabName));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void CaptureEntry_ReusesActorSnapshotFields()
|
||||
{
|
||||
var prefab = AssetDatabase.LoadAssetAtPath<GameObject>(PrefabPath);
|
||||
var actorObject = Object.Instantiate(prefab);
|
||||
var slotObject = new GameObject("Slot");
|
||||
try
|
||||
{
|
||||
var slot = slotObject.AddComponent<ActorSlot>();
|
||||
slot.slotName = "test-slot";
|
||||
slot.sortingLayer = 0;
|
||||
slot.sortingOrder = 17;
|
||||
var actor = actorObject.GetComponent<FrameAnimationActor>();
|
||||
actor.Init("TestActor", slot, ActorType.FrameAnimation);
|
||||
actor.Show();
|
||||
|
||||
ActorEntrySnapshotDto entry = actor.CaptureEntry();
|
||||
|
||||
Assert.That(entry.actorName, Is.EqualTo("TestActor"));
|
||||
Assert.That(entry.slotName, Is.EqualTo("test-slot"));
|
||||
Assert.That(entry.actorType, Is.EqualTo(nameof(ActorType.FrameAnimation)));
|
||||
Assert.That(entry.alpha, Is.EqualTo(1f));
|
||||
Assert.That(entry.stateName, Is.Empty);
|
||||
}
|
||||
finally
|
||||
{
|
||||
Object.DestroyImmediate(actorObject);
|
||||
Object.DestroyImmediate(slotObject);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
+1
-1
@@ -1,5 +1,5 @@
|
||||
fileFormatVersion: 2
|
||||
guid: c9dd51810ef0cc94398175f3597a978e
|
||||
guid: 23aa0707ef5c4618b55c455f2a03a612
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
@@ -0,0 +1,577 @@
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using System.Text.RegularExpressions;
|
||||
using System.Threading.Tasks;
|
||||
using AibisDream.Framework;
|
||||
using AibisDream.MiniGame.Language;
|
||||
using AibisDream.Utility;
|
||||
using NUnit.Framework;
|
||||
using UnityEditor;
|
||||
using UnityEditor.Localization;
|
||||
using UnityEngine;
|
||||
using UnityEngine.Localization;
|
||||
using UnityEngine.Localization.Settings;
|
||||
using UnityEngine.Localization.Tables;
|
||||
using UnityEngine.TestTools;
|
||||
|
||||
namespace AibisDream.EditorTests.Huoshan
|
||||
{
|
||||
public sealed class ExpressionLocalizationTests
|
||||
{
|
||||
private const string CatalogPath =
|
||||
"Assets/GameContent/Feature_Huoshan/Expression/ExpressionContentCatalog.asset";
|
||||
private const string ParticleProfilePath =
|
||||
"Assets/GameContent/Feature_Huoshan/Expression/ExpressionParticleLanguageProfile.asset";
|
||||
|
||||
[TestCase(null, false)]
|
||||
[TestCase("", false)]
|
||||
[TestCase("plain", false)]
|
||||
[TestCase("L10N.key", false)]
|
||||
[TestCase("l10n.key", true)]
|
||||
public void LocalizedParamPrefix_IsStrict(string value, bool expected)
|
||||
{
|
||||
Assert.That(LocalizationKit.IsLocalizedParam(value), Is.EqualTo(expected));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void LocalizedParamKey_StripsOnlyKnownPrefix()
|
||||
{
|
||||
Assert.That(LocalizationKit.GetL10NParamKey("l10n.hs.exp.log1.target"),
|
||||
Is.EqualTo("hs.exp.log1.target"));
|
||||
Assert.That(LocalizationKit.GetL10NParamKey("raw"), Is.EqualTo("raw"));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void MissingParamResult_RequiresMatchingLocalizedReference()
|
||||
{
|
||||
Assert.That(
|
||||
LocalizationKit.IsMissingParamResult(
|
||||
"l10n.hs.exp.pool",
|
||||
"⟦hs.exp.pool⟧"),
|
||||
Is.True);
|
||||
Assert.That(
|
||||
LocalizationKit.IsMissingParamResult(
|
||||
"l10n.hs.exp.pool",
|
||||
"⟦another.key⟧"),
|
||||
Is.False);
|
||||
Assert.That(
|
||||
LocalizationKit.IsMissingParamResult(
|
||||
"raw",
|
||||
"⟦raw⟧"),
|
||||
Is.False);
|
||||
}
|
||||
|
||||
[UnityTest]
|
||||
public IEnumerator LocalizeParamAsync_UsesExplicitLocaleAndDoesNotFallback()
|
||||
{
|
||||
Locale chinese = LocalizationEditorSettings.GetLocale(
|
||||
new LocaleIdentifier("zh-Hans"));
|
||||
Locale english = LocalizationEditorSettings.GetLocale(
|
||||
new LocaleIdentifier("en"));
|
||||
Assert.That(chinese, Is.Not.Null);
|
||||
Assert.That(english, Is.Not.Null);
|
||||
|
||||
Locale previous = LocalizationSettings.SelectedLocale;
|
||||
LocalizationSettings.SelectedLocale = english;
|
||||
Task<string> chineseTask = LocalizationKit.LocalizeParamAsync(
|
||||
"l10n.hs.exp.log1.target",
|
||||
chinese);
|
||||
Task<string> englishTask = LocalizationKit.LocalizeParamAsync(
|
||||
"l10n.hs.exp.log1.target",
|
||||
english);
|
||||
|
||||
while (!chineseTask.IsCompleted || !englishTask.IsCompleted)
|
||||
yield return null;
|
||||
|
||||
LocalizationSettings.SelectedLocale = previous;
|
||||
Assert.That(chineseTask.Result, Is.EqualTo("我就是个笑话"));
|
||||
Assert.That(englishTask.Result, Is.EqualTo("I am nothing but a joke"));
|
||||
}
|
||||
|
||||
[UnityTest]
|
||||
public IEnumerator LocalizeParamAsync_ReturnsRawTextAndMissingKeyMarker()
|
||||
{
|
||||
Task<string> rawTask = LocalizationKit.LocalizeParamAsync("raw text");
|
||||
while (!rawTask.IsCompleted)
|
||||
yield return null;
|
||||
Assert.That(rawTask.Result, Is.EqualTo("raw text"));
|
||||
|
||||
Locale chinese = LocalizationEditorSettings.GetLocale(
|
||||
new LocaleIdentifier("zh-Hans"));
|
||||
LogAssert.Expect(
|
||||
LogType.Error,
|
||||
new Regex(@"Params 缺少条目.*Key=hs\.exp\.missing.*Locale=zh-Hans"));
|
||||
Task<string> missingTask = LocalizationKit.LocalizeParamAsync(
|
||||
"l10n.hs.exp.missing",
|
||||
chinese);
|
||||
while (!missingTask.IsCompleted)
|
||||
yield return null;
|
||||
Assert.That(missingTask.Result, Is.EqualTo("⟦hs.exp.missing⟧"));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void LegacyCommonParams_ArePresentInUnityParams()
|
||||
{
|
||||
StringTableCollection collection =
|
||||
LocalizationEditorSettings.GetStringTableCollection(ConstRef.ParamsTable);
|
||||
Assert.That(collection, Is.Not.Null);
|
||||
|
||||
string[] legacyKeys = { "天空", "海报", "植物", "朋友", "情绪", "记忆", "逻辑" };
|
||||
foreach (string key in legacyKeys)
|
||||
Assert.That(collection.SharedData.GetEntry(key), Is.Not.Null, key);
|
||||
|
||||
Assert.That(AssetDatabase.LoadAssetAtPath<TextAsset>(
|
||||
"Assets/StreamingAssets/Config/params.csv"), Is.Null);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void ExpressionCatalog_IsValidAndCaseSensitive()
|
||||
{
|
||||
ExpressionContentCatalog catalog =
|
||||
AssetDatabase.LoadAssetAtPath<ExpressionContentCatalog>(CatalogPath);
|
||||
Assert.That(catalog, Is.Not.Null);
|
||||
Assert.That(catalog.GetValidationErrors(), Is.Empty);
|
||||
Assert.That(catalog.Rounds.Select(round => round.Id),
|
||||
Is.EqualTo(new[] { "log1", "log2", "log3" }));
|
||||
|
||||
Assert.That(catalog.TryGetRound("log1", out ExpressionRoundDefinition log1), Is.True);
|
||||
Assert.That(log1.NonTargetParticleCount, Is.EqualTo(16));
|
||||
Assert.That(catalog.TryGetRound("LOG1", out _), Is.False);
|
||||
Assert.That(catalog.TryGetRound("missing", out _), Is.False);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void ExpressionCatalog_RejectsDuplicateIdsAndNonLocalizedReferences()
|
||||
{
|
||||
ExpressionContentCatalog catalog =
|
||||
ScriptableObject.CreateInstance<ExpressionContentCatalog>();
|
||||
try
|
||||
{
|
||||
var first = new ExpressionRoundDefinition();
|
||||
var second = new ExpressionRoundDefinition();
|
||||
ConfigureRound(first, "log1", "l10n.target", "l10n.tokens", 1);
|
||||
ConfigureRound(second, "log1", "raw", "l10n.tokens", 1);
|
||||
SetPrivateField(
|
||||
catalog,
|
||||
"rounds",
|
||||
new List<ExpressionRoundDefinition> { first, second });
|
||||
|
||||
string errors = string.Join("\n", catalog.GetValidationErrors());
|
||||
Assert.That(errors, Does.Contain("重复"));
|
||||
Assert.That(errors, Does.Contain("l10n.*"));
|
||||
}
|
||||
finally
|
||||
{
|
||||
Object.DestroyImmediate(catalog);
|
||||
}
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TokenParser_TrimsDropsEmptyAndRejectsFullwidthSeparator()
|
||||
{
|
||||
Assert.That(
|
||||
LanguageYarnCommand.TryParseExpressionTokens(
|
||||
" 哈哈 | | 嘿嘿|呵呵 ",
|
||||
out var tokens),
|
||||
Is.True);
|
||||
Assert.That(tokens, Is.EqualTo(new[] { "哈哈", "嘿嘿", "呵呵" }));
|
||||
|
||||
LogAssert.Expect(LogType.Error, new Regex("全角分隔符"));
|
||||
Assert.That(
|
||||
LanguageYarnCommand.TryParseExpressionTokens("哈哈|嘿嘿", out _),
|
||||
Is.False);
|
||||
Assert.That(
|
||||
LanguageYarnCommand.TryParseExpressionTokens(" | | ", out _),
|
||||
Is.False);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void CharacterPoolParser_PreservesUnicodeDuplicatesAndRejectsSeparators()
|
||||
{
|
||||
string combining = "e\u0301";
|
||||
Assert.That(
|
||||
LanguageYarnCommand.TryParseExpressionPool(
|
||||
$" 中 中 あ {combining} 😀。 ",
|
||||
out List<string> pool),
|
||||
Is.True);
|
||||
Assert.That(
|
||||
pool,
|
||||
Is.EqualTo(new[] { "中", "中", "あ", combining, "😀", "。" }));
|
||||
Assert.That(
|
||||
LanguageYarnCommand.TryParseExpressionPool("中|あ", out _),
|
||||
Is.False);
|
||||
Assert.That(
|
||||
LanguageYarnCommand.TryParseExpressionPool("中|あ", out _),
|
||||
Is.False);
|
||||
Assert.That(
|
||||
LanguageYarnCommand.TryParseExpressionPool(" \t ", out _),
|
||||
Is.False);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void ParticleLanguageProfile_ResolvesExactPrefixAndFallbackModes()
|
||||
{
|
||||
ExpressionParticleLanguageProfile profile =
|
||||
AssetDatabase.LoadAssetAtPath<ExpressionParticleLanguageProfile>(
|
||||
ParticleProfilePath);
|
||||
Assert.That(profile, Is.Not.Null);
|
||||
Assert.That(profile.GetValidationErrors(), Is.Empty);
|
||||
|
||||
Assert.That(
|
||||
profile.Resolve(new LocaleIdentifier("zh-Hans")).UnitMode,
|
||||
Is.EqualTo(ExpressionParticleUnitMode.Grapheme));
|
||||
Assert.That(
|
||||
profile.Resolve(new LocaleIdentifier("ja-JP")).UnitMode,
|
||||
Is.EqualTo(ExpressionParticleUnitMode.Grapheme));
|
||||
|
||||
foreach (string localeCode in new[] { "en", "es", "ru", "pt-BR" })
|
||||
{
|
||||
Assert.That(
|
||||
profile.Resolve(new LocaleIdentifier(localeCode)).UnitMode,
|
||||
Is.EqualTo(ExpressionParticleUnitMode.Word),
|
||||
localeCode);
|
||||
}
|
||||
|
||||
Assert.That(
|
||||
profile.Resolve(new LocaleIdentifier("en-US")).LocaleCode,
|
||||
Is.EqualTo("en"));
|
||||
|
||||
LogAssert.Expect(
|
||||
LogType.Warning,
|
||||
new Regex(@"Locale 'zz-ZZ'.*Grapheme"));
|
||||
Assert.That(
|
||||
profile.Resolve(new LocaleIdentifier("zz-ZZ")).UnitMode,
|
||||
Is.EqualTo(ExpressionParticleUnitMode.Grapheme));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void WordTokenizer_UsesWhitespaceBoundariesAndKeepsPunctuation()
|
||||
{
|
||||
Assert.That(
|
||||
ExpressionTextTokenizer.TokenizeText(
|
||||
"I am not a joke",
|
||||
ExpressionParticleUnitMode.Word),
|
||||
Is.EqualTo(new[] { "I", "am", "not", "a", "joke" }));
|
||||
Assert.That(
|
||||
ExpressionTextTokenizer.TokenizeText(
|
||||
"don't self-doubt ¿Por qué? веришь?",
|
||||
ExpressionParticleUnitMode.Word),
|
||||
Is.EqualTo(new[]
|
||||
{
|
||||
"don't",
|
||||
"self-doubt",
|
||||
"¿Por",
|
||||
"qué?",
|
||||
"веришь?"
|
||||
}));
|
||||
Assert.That(
|
||||
ExpressionTextTokenizer.TokenizeText(
|
||||
"one \t\r\n two",
|
||||
ExpressionParticleUnitMode.Word),
|
||||
Is.EqualTo(new[] { "one", "two" }));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void GraphemeTokenizer_PreservesCjkKanaAndCombiningCharacters()
|
||||
{
|
||||
const string combining = "e\u0301";
|
||||
Assert.That(
|
||||
ExpressionTextTokenizer.TokenizeText(
|
||||
$"汉 あ {combining}",
|
||||
ExpressionParticleUnitMode.Grapheme),
|
||||
Is.EqualTo(new[] { "汉", "あ", combining }));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void WordPool_PreservesDuplicateWeightsAndRejectsPipeSeparators()
|
||||
{
|
||||
Assert.That(
|
||||
LanguageYarnCommand.TryParseExpressionPool(
|
||||
"noise doubt noise",
|
||||
ExpressionParticleUnitMode.Word,
|
||||
out List<string> pool),
|
||||
Is.True);
|
||||
Assert.That(pool, Is.EqualTo(new[] { "noise", "doubt", "noise" }));
|
||||
Assert.That(
|
||||
LanguageYarnCommand.TryParseExpressionPool(
|
||||
"noise|doubt",
|
||||
ExpressionParticleUnitMode.Word,
|
||||
out _),
|
||||
Is.False);
|
||||
Assert.That(
|
||||
LanguageYarnCommand.TryParseExpressionPool(
|
||||
"noise|doubt",
|
||||
ExpressionParticleUnitMode.Word,
|
||||
out _),
|
||||
Is.False);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void RoundSnapshot_IsParsedOnceAndKeepsItsLocaleAndWordUnits()
|
||||
{
|
||||
ExpressionParticleLanguageProfile languageProfile =
|
||||
AssetDatabase.LoadAssetAtPath<ExpressionParticleLanguageProfile>(
|
||||
ParticleProfilePath);
|
||||
ExpressionParticleLocaleSettings english =
|
||||
languageProfile.Resolve(new LocaleIdentifier("en"));
|
||||
Assert.That(
|
||||
ExpressionRoundTextSnapshot.TryCreate(
|
||||
new LocaleIdentifier("en"),
|
||||
english,
|
||||
"I am not a joke",
|
||||
new[] { "false alarm", "false alarm" },
|
||||
"noise doubt noise",
|
||||
out ExpressionRoundTextSnapshot snapshot,
|
||||
out string error),
|
||||
Is.True,
|
||||
error);
|
||||
|
||||
Locale previous = LocalizationSettings.SelectedLocale;
|
||||
try
|
||||
{
|
||||
LocalizationSettings.SelectedLocale =
|
||||
LocalizationEditorSettings.GetLocale(
|
||||
new LocaleIdentifier("zh-Hans"));
|
||||
|
||||
Assert.That(snapshot.Locale.Code, Is.EqualTo("en"));
|
||||
Assert.That(
|
||||
snapshot.TargetUnits,
|
||||
Is.EqualTo(new[] { "I", "am", "not", "a", "joke" }));
|
||||
Assert.That(
|
||||
snapshot.InterferencePoolUnits,
|
||||
Is.EqualTo(new[] { "false", "alarm", "false", "alarm" }));
|
||||
Assert.That(
|
||||
snapshot.DefaultPoolUnits,
|
||||
Is.EqualTo(new[] { "noise", "doubt", "noise" }));
|
||||
}
|
||||
finally
|
||||
{
|
||||
LocalizationSettings.SelectedLocale = previous;
|
||||
}
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void WordProfile_ScalesConfiguredNonTargetCounts()
|
||||
{
|
||||
ExpressionParticleLanguageProfile profile =
|
||||
AssetDatabase.LoadAssetAtPath<ExpressionParticleLanguageProfile>(
|
||||
ParticleProfilePath);
|
||||
ExpressionParticleLocaleSettings english =
|
||||
profile.Resolve(new LocaleIdentifier("en"));
|
||||
|
||||
Assert.That(english.ScaleNonTargetCount(16), Is.EqualTo(10));
|
||||
Assert.That(english.ScaleNonTargetCount(28), Is.EqualTo(18));
|
||||
Assert.That(english.ScaleNonTargetCount(38), Is.EqualTo(25));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void ParticleGeometry_UsesVisualEdgeDistanceAndShortestSeparationAxis()
|
||||
{
|
||||
var left = new Bounds(Vector3.zero, new Vector3(4f, 1f, 0.01f));
|
||||
var right = new Bounds(
|
||||
new Vector3(4.15f, 0f, 0f),
|
||||
new Vector3(4f, 1f, 0.01f));
|
||||
|
||||
Assert.That(
|
||||
ExpressionParticleGeometry.BoundsDistance(left, right),
|
||||
Is.EqualTo(0.15f).Within(0.0001f));
|
||||
|
||||
right.center = new Vector3(3.8f, 0f, 0f);
|
||||
Assert.That(
|
||||
ExpressionParticleGeometry.TryGetSeparation(
|
||||
left,
|
||||
right,
|
||||
0.1f,
|
||||
out Vector2 direction,
|
||||
out float overlap),
|
||||
Is.True);
|
||||
Assert.That(direction, Is.EqualTo(Vector2.left));
|
||||
Assert.That(overlap, Is.EqualTo(0.3f).Within(0.0001f));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TextParticle_VisualBoundsIncludesTheWholeWordForPointerDistance()
|
||||
{
|
||||
var gameObject = new GameObject("TextParticleBoundsTest");
|
||||
try
|
||||
{
|
||||
CandidateParticle particle =
|
||||
gameObject.AddComponent<CandidateParticle>();
|
||||
particle.ForceSetUnitText("self-doubt");
|
||||
Bounds bounds = particle.GetVisualWorldBounds();
|
||||
|
||||
Assert.That(bounds.size.x, Is.GreaterThan(0f));
|
||||
Assert.That(
|
||||
particle.DistanceToVisualBounds(
|
||||
new Vector2(bounds.max.x, bounds.center.y)),
|
||||
Is.EqualTo(0f).Within(0.0001f));
|
||||
}
|
||||
finally
|
||||
{
|
||||
Object.DestroyImmediate(gameObject);
|
||||
}
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void Params_ContainsEnglishWordPoolAndPendingPoolsForOtherLocales()
|
||||
{
|
||||
StringTableCollection collection =
|
||||
LocalizationEditorSettings.GetStringTableCollection(ConstRef.ParamsTable);
|
||||
Assert.That(collection, Is.Not.Null);
|
||||
|
||||
string key =
|
||||
LocalizationKit.GetL10NParamKey(ConstRef.ExpressParticlePoolParam);
|
||||
Assert.That(collection.SharedData.GetEntry(key), Is.Not.Null);
|
||||
|
||||
string[] localeCodes = { "zh-Hans", "en", "ja-JP", "es", "ru", "pt-BR" };
|
||||
foreach (string localeCode in localeCodes)
|
||||
{
|
||||
StringTable table =
|
||||
collection.GetTable(new LocaleIdentifier(localeCode)) as StringTable;
|
||||
Assert.That(table, Is.Not.Null, localeCode);
|
||||
Assert.That(table.GetEntry(key), Is.Not.Null, localeCode);
|
||||
}
|
||||
|
||||
StringTable chinese =
|
||||
collection.GetTable(new LocaleIdentifier("zh-Hans")) as StringTable;
|
||||
Assert.That(
|
||||
ExpressionTextTokenizer.GetVisibleElements(
|
||||
chinese.GetEntry(key).LocalizedValue),
|
||||
Is.Not.Empty);
|
||||
|
||||
StringTable english =
|
||||
collection.GetTable(new LocaleIdentifier("en")) as StringTable;
|
||||
Assert.That(
|
||||
LanguageYarnCommand.TryParseExpressionPool(
|
||||
english.GetEntry(key).LocalizedValue,
|
||||
ExpressionParticleUnitMode.Word,
|
||||
out List<string> englishPool),
|
||||
Is.True);
|
||||
Assert.That(englishPool, Does.Contain("truth"));
|
||||
Assert.That(englishPool, Does.Contain("believe"));
|
||||
|
||||
foreach (string localeCode in localeCodes.Skip(2))
|
||||
{
|
||||
StringTable table =
|
||||
collection.GetTable(new LocaleIdentifier(localeCode)) as StringTable;
|
||||
Assert.That(table.GetEntry(key).LocalizedValue, Is.Empty, localeCode);
|
||||
}
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TextParticle_UsesConfiguredRoleAndOverridePoolsWithoutFirstFrameFallback()
|
||||
{
|
||||
var gameObject = new GameObject("TextParticlePoolTest");
|
||||
try
|
||||
{
|
||||
CandidateParticle particle = gameObject.AddComponent<CandidateParticle>();
|
||||
Assert.That(particle.CurrentUnitText, Is.Null.Or.Empty);
|
||||
|
||||
string combining = "e\u0301";
|
||||
particle.SetUnitPools(
|
||||
new[] { "😀" },
|
||||
new[] { combining });
|
||||
|
||||
particle.originalIsRed = true;
|
||||
particle.InitializeRandomUnit();
|
||||
Assert.That(particle.CurrentUnitText, Is.EqualTo("😀"));
|
||||
|
||||
particle.originalIsRed = false;
|
||||
particle.InitializeRandomUnit();
|
||||
Assert.That(particle.CurrentUnitText, Is.EqualTo(combining));
|
||||
|
||||
particle.SetOverrideUnitPool(new[] { "。" });
|
||||
particle.InitializeRandomUnit();
|
||||
Assert.That(particle.CurrentUnitText, Is.EqualTo("。"));
|
||||
|
||||
particle.SetOverrideUnitPool((IReadOnlyList<string>)null);
|
||||
particle.InitializeRandomUnit();
|
||||
Assert.That(particle.CurrentUnitText, Is.EqualTo(combining));
|
||||
}
|
||||
finally
|
||||
{
|
||||
Object.DestroyImmediate(gameObject);
|
||||
}
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void UnicodeTokenizer_PreservesTextElementsAndDropsWhitespace()
|
||||
{
|
||||
string combining = "e\u0301";
|
||||
var elements =
|
||||
ExpressionTextTokenizer.GetVisibleElements($"中 A あ {combining} 😀。");
|
||||
|
||||
Assert.That(
|
||||
elements,
|
||||
Is.EqualTo(new[] { "中", "A", "あ", combining, "😀", "。" }));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void UnicodeLayout_PreservesWordGapWithoutWhitespaceParticle()
|
||||
{
|
||||
ExpressionTextTokenizer.Layout layout =
|
||||
ExpressionTextTokenizer.BuildLayout("A B", 1f, 0.6f);
|
||||
|
||||
Assert.That(layout.VisibleElements, Is.EqualTo(new[] { "A", "B" }));
|
||||
Assert.That(layout.Offsets[0], Is.EqualTo(-0.8f).Within(0.0001f));
|
||||
Assert.That(layout.Offsets[1], Is.EqualTo(0.8f).Within(0.0001f));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void UnicodeSequenceMatch_UsesWholeTextElements()
|
||||
{
|
||||
var source = ExpressionTextTokenizer.GetVisibleElements("A😀e\u0301。");
|
||||
var fragment = ExpressionTextTokenizer.GetVisibleElements("😀e\u0301");
|
||||
Assert.That(ExpressionTextTokenizer.FindVisibleSequence(source, fragment),
|
||||
Is.EqualTo(1));
|
||||
Assert.That(
|
||||
ExpressionTextTokenizer.FindVisibleSequence(
|
||||
source,
|
||||
ExpressionTextTokenizer.GetVisibleElements("😀x")),
|
||||
Is.EqualTo(-1));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void ExpressContentValidation_HasNoBlockingErrors()
|
||||
{
|
||||
var issues = ExpressionLocalizationValidator.Validate();
|
||||
string errors = string.Join(
|
||||
"\n",
|
||||
issues
|
||||
.Where(issue =>
|
||||
issue.Severity == ExpressionLocalizationIssueSeverity.Error)
|
||||
.Select(issue => issue.Message));
|
||||
|
||||
Assert.That(errors, Is.Empty);
|
||||
Assert.That(
|
||||
issues.Any(issue =>
|
||||
issue.Severity == ExpressionLocalizationIssueSeverity.Warning),
|
||||
Is.True,
|
||||
"未翻译 Locale 的空条目应作为待翻译警告保留。");
|
||||
}
|
||||
|
||||
private static void ConfigureRound(
|
||||
ExpressionRoundDefinition round,
|
||||
string id,
|
||||
string target,
|
||||
string tokens,
|
||||
int nonTargetCount)
|
||||
{
|
||||
SetPrivateField(round, "id", id);
|
||||
SetPrivateField(round, "targetReference", target);
|
||||
SetPrivateField(round, "tokenReference", tokens);
|
||||
SetPrivateField(round, "nonTargetParticleCount", nonTargetCount);
|
||||
}
|
||||
|
||||
private static void SetPrivateField(object target, string fieldName, object value)
|
||||
{
|
||||
FieldInfo field = target.GetType().GetField(
|
||||
fieldName,
|
||||
BindingFlags.Instance | BindingFlags.NonPublic);
|
||||
Assert.That(field, Is.Not.Null, fieldName);
|
||||
field.SetValue(target, value);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: d6b8797332ac4876b66e381ea3c98723
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,216 @@
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Reflection;
|
||||
using AibisDream.MiniGame.Language;
|
||||
using NUnit.Framework;
|
||||
using UnityEngine;
|
||||
|
||||
namespace AibisDream.EditorTests.Huoshan
|
||||
{
|
||||
public sealed class LogReleasePresentationTests
|
||||
{
|
||||
[Test]
|
||||
public void LineClip_RejectsOutsideSegment()
|
||||
{
|
||||
Bounds bounds = new Bounds(Vector3.zero, new Vector3(2f, 2f, 0f));
|
||||
bool visible = ConnectionRenderer.TryClipLineToBounds(
|
||||
new Vector3(-3f, 2f),
|
||||
new Vector3(3f, 2f),
|
||||
bounds,
|
||||
out _,
|
||||
out _);
|
||||
|
||||
Assert.That(visible, Is.False);
|
||||
}
|
||||
|
||||
[TestCase(-0.5f, 0f, 0.5f, 0f, -0.5f, 0f, 0.5f, 0f)]
|
||||
[TestCase(-2f, 0f, 2f, 0f, -1f, 0f, 1f, 0f)]
|
||||
[TestCase(0f, -2f, 0f, 0.5f, 0f, -1f, 0f, 0.5f)]
|
||||
[TestCase(-2f, -2f, 2f, 2f, -1f, -1f, 1f, 1f)]
|
||||
public void LineClip_ReturnsExpectedInteriorSegment(
|
||||
float ax,
|
||||
float ay,
|
||||
float bx,
|
||||
float by,
|
||||
float expectedAx,
|
||||
float expectedAy,
|
||||
float expectedBx,
|
||||
float expectedBy)
|
||||
{
|
||||
Bounds bounds = new Bounds(Vector3.zero, new Vector3(2f, 2f, 0f));
|
||||
bool visible = ConnectionRenderer.TryClipLineToBounds(
|
||||
new Vector3(ax, ay),
|
||||
new Vector3(bx, by),
|
||||
bounds,
|
||||
out Vector3 clippedA,
|
||||
out Vector3 clippedB);
|
||||
|
||||
Assert.That(visible, Is.True);
|
||||
Assert.That(clippedA.x, Is.EqualTo(expectedAx).Within(0.0001f));
|
||||
Assert.That(clippedA.y, Is.EqualTo(expectedAy).Within(0.0001f));
|
||||
Assert.That(clippedB.x, Is.EqualTo(expectedBx).Within(0.0001f));
|
||||
Assert.That(clippedB.y, Is.EqualTo(expectedBy).Within(0.0001f));
|
||||
}
|
||||
|
||||
[TestCase(CompletionPhase.None, false)]
|
||||
[TestCase(CompletionPhase.Completed, false)]
|
||||
[TestCase(CompletionPhase.Focusing, true)]
|
||||
[TestCase(CompletionPhase.FocusHolding, true)]
|
||||
[TestCase(CompletionPhase.Finished, true)]
|
||||
public void ConnectionClipping_OnlyStartsAfterOutput(
|
||||
CompletionPhase phase,
|
||||
bool expected)
|
||||
{
|
||||
Assert.That(LanguageParticleManager.ShouldClipConnections(phase), Is.EqualTo(expected));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void LieEdgePosition_LandsOnScreenPerimeterAndStaysInside()
|
||||
{
|
||||
Bounds bounds = new Bounds(Vector3.zero, new Vector3(6f, 4f, 0f));
|
||||
const float inset = 0.1f;
|
||||
for (int i = 0; i < 8; i++)
|
||||
{
|
||||
Vector3 position = LanguageParticleManager.CalculateScreenEdgePosition(
|
||||
bounds,
|
||||
i,
|
||||
8,
|
||||
inset);
|
||||
|
||||
Assert.That(position.x, Is.InRange(bounds.min.x, bounds.max.x));
|
||||
Assert.That(position.y, Is.InRange(bounds.min.y, bounds.max.y));
|
||||
bool touchesVerticalEdge =
|
||||
Mathf.Abs(Mathf.Abs(position.x) - (bounds.extents.x - inset)) < 0.0001f;
|
||||
bool touchesHorizontalEdge =
|
||||
Mathf.Abs(Mathf.Abs(position.y) - (bounds.extents.y - inset)) < 0.0001f;
|
||||
Assert.That(touchesVerticalEdge || touchesHorizontalEdge, Is.True);
|
||||
}
|
||||
}
|
||||
|
||||
[TestCase(6, 0.70f)]
|
||||
[TestCase(6, 0.90f)]
|
||||
[TestCase(6, 1.10f)]
|
||||
public void ResolveTiming_LastCharacterCompletesWithinPreset(int characterCount, float totalDuration)
|
||||
{
|
||||
LanguageParticleManager.CalculateResolveTiming(
|
||||
characterCount,
|
||||
totalDuration,
|
||||
3.5f,
|
||||
0.4f,
|
||||
out float characterDuration,
|
||||
out float stagger);
|
||||
|
||||
float lastCompletion = characterDuration + stagger * (characterCount - 1);
|
||||
Assert.That(lastCompletion, Is.LessThanOrEqualTo(totalDuration + 0.0001f));
|
||||
Assert.That(lastCompletion, Is.EqualTo(totalDuration).Within(0.0001f));
|
||||
}
|
||||
|
||||
[TestCase("LightPreset", 0.10f)]
|
||||
[TestCase("MediumPreset", 0.20f)]
|
||||
[TestCase("HeavyPreset", 0.35f)]
|
||||
public void PresentationPreset_UsesApprovedValues(
|
||||
string presetFieldName,
|
||||
float baseDistortion)
|
||||
{
|
||||
const BindingFlags flags = BindingFlags.Static | BindingFlags.NonPublic;
|
||||
FieldInfo presetField = typeof(LogReleasePresentationController).GetField(presetFieldName, flags);
|
||||
Assert.That(presetField, Is.Not.Null);
|
||||
object preset = presetField.GetValue(null);
|
||||
Type presetType = preset.GetType();
|
||||
|
||||
AssertPresetField(presetType, preset, "BaseDistortion", baseDistortion);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TmpClipRect_ConvertsWorldCornersIntoTextLocalSpace()
|
||||
{
|
||||
GameObject rectObject = new GameObject("ScreenRect", typeof(RectTransform));
|
||||
GameObject textObject = new GameObject("TextTransform");
|
||||
try
|
||||
{
|
||||
RectTransform rect = rectObject.GetComponent<RectTransform>();
|
||||
rect.sizeDelta = new Vector2(4f, 2f);
|
||||
rect.position = new Vector3(2f, -1f, 0f);
|
||||
rect.rotation = Quaternion.Euler(0f, 0f, 17f);
|
||||
rect.localScale = new Vector3(1.2f, 0.8f, 1f);
|
||||
|
||||
textObject.transform.position = new Vector3(-0.5f, 0.75f, 0f);
|
||||
textObject.transform.rotation = Quaternion.Euler(0f, 0f, -11f);
|
||||
textObject.transform.localScale = new Vector3(1.4f, 0.65f, 1f);
|
||||
|
||||
Vector4 actual = TMPRectClipper.CalculateLocalClipRect(textObject.transform, rect);
|
||||
Vector3[] corners = new Vector3[4];
|
||||
rect.GetWorldCorners(corners);
|
||||
Vector3 first = textObject.transform.InverseTransformPoint(corners[0]);
|
||||
float minX = first.x;
|
||||
float minY = first.y;
|
||||
float maxX = first.x;
|
||||
float maxY = first.y;
|
||||
for (int i = 1; i < corners.Length; i++)
|
||||
{
|
||||
Vector3 local = textObject.transform.InverseTransformPoint(corners[i]);
|
||||
minX = Mathf.Min(minX, local.x);
|
||||
minY = Mathf.Min(minY, local.y);
|
||||
maxX = Mathf.Max(maxX, local.x);
|
||||
maxY = Mathf.Max(maxY, local.y);
|
||||
}
|
||||
|
||||
Assert.That(actual.x, Is.EqualTo(minX).Within(0.0001f));
|
||||
Assert.That(actual.y, Is.EqualTo(minY).Within(0.0001f));
|
||||
Assert.That(actual.z, Is.EqualTo(maxX).Within(0.0001f));
|
||||
Assert.That(actual.w, Is.EqualTo(maxY).Within(0.0001f));
|
||||
}
|
||||
finally
|
||||
{
|
||||
UnityEngine.Object.DestroyImmediate(rectObject);
|
||||
UnityEngine.Object.DestroyImmediate(textObject);
|
||||
}
|
||||
}
|
||||
|
||||
[TestCase(null, null)]
|
||||
[TestCase("", null)]
|
||||
[TestCase(" | ", null)]
|
||||
[TestCase("没问题|冇", "没问题冇")]
|
||||
[TestCase("没 问 题", "没问题")]
|
||||
public void SlotMachinePool_StripsSeparatorsAndWhitespace(string input, string expected)
|
||||
{
|
||||
Assert.That(LanguageParticleManager.SanitizeSlotMachinePool(input), Is.EqualTo(expected));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void ActorPhraseList_PreservesWordsAndDropsEmptyEntries()
|
||||
{
|
||||
string[] phrases = LogReleasePresentationController.ParseActorPhrases(
|
||||
" 没问题 | | 冇问题|帽问题 ");
|
||||
|
||||
Assert.That(phrases, Is.EqualTo(new[] { "没问题", "冇问题", "帽问题" }));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void ActorFlash_OutsideMemoryState_CompletesImmediatelyWithoutStateChange()
|
||||
{
|
||||
GameObject host = new GameObject("PresentationHost");
|
||||
try
|
||||
{
|
||||
var controller = host.AddComponent<LogReleasePresentationController>();
|
||||
IEnumerator routine = controller.FlashActorLie("没问题", 0.6f);
|
||||
|
||||
Assert.That(routine.MoveNext(), Is.False);
|
||||
Assert.That(
|
||||
controller.State,
|
||||
Is.EqualTo(LogReleasePresentationController.PresentationState.Idle));
|
||||
}
|
||||
finally
|
||||
{
|
||||
UnityEngine.Object.DestroyImmediate(host);
|
||||
}
|
||||
}
|
||||
|
||||
private static void AssertPresetField(Type presetType, object preset, string name, float expected)
|
||||
{
|
||||
FieldInfo field = presetType.GetField(name, BindingFlags.Instance | BindingFlags.Public);
|
||||
Assert.That(field, Is.Not.Null);
|
||||
Assert.That((float)field.GetValue(preset), Is.EqualTo(expected).Within(0.0001f));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 5f75b14634314395a3a67721065f86bd
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,44 @@
|
||||
using AibisDream.Utility;
|
||||
using NUnit.Framework;
|
||||
using Yarn.Unity;
|
||||
|
||||
namespace AibisDream.SystemEditor.Tests
|
||||
{
|
||||
public sealed class OptionPromptMetadataTests
|
||||
{
|
||||
[Test]
|
||||
public void IsOptionPromptLine_WithExactMetadata_ReturnsTrue()
|
||||
{
|
||||
var line = new LocalizedLine
|
||||
{
|
||||
Metadata = new[] { "line:0123456", YarnUtil.OptionPrompt }
|
||||
};
|
||||
|
||||
Assert.That(line.IsOptionPromptLine(), Is.True);
|
||||
}
|
||||
|
||||
[TestCase("option_prompts")]
|
||||
[TestCase("dream_option_prompt")]
|
||||
[TestCase("OPTION_PROMPT")]
|
||||
public void IsOptionPromptLine_WithSimilarMetadata_ReturnsFalse(string metadata)
|
||||
{
|
||||
var line = new LocalizedLine
|
||||
{
|
||||
Metadata = new[] { metadata }
|
||||
};
|
||||
|
||||
Assert.That(line.IsOptionPromptLine(), Is.False);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void IsOptionPromptLine_WithoutMetadata_ReturnsFalse()
|
||||
{
|
||||
var line = new LocalizedLine
|
||||
{
|
||||
Metadata = null
|
||||
};
|
||||
|
||||
Assert.That(line.IsOptionPromptLine(), Is.False);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: d5e819fca9154e979c37ac490184620b
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
+53
-27
@@ -5,10 +5,10 @@ using NUnit.Framework;
|
||||
|
||||
namespace AibisDream.SystemEditor.Tests
|
||||
{
|
||||
public sealed class SaveSnapshotContractTests
|
||||
public sealed class PresentationSnapshotContractTests
|
||||
{
|
||||
[Test]
|
||||
public void PresentationProviders_KeepStableIdsAndRestoreOrder()
|
||||
public void PresentationProviders_UseStableIdsAndRestoreInDependencyOrder()
|
||||
{
|
||||
var showcase = new ShowcaseSnapshotProvider();
|
||||
var day2Sleep = new Day2SleepPresentationSnapshotProvider();
|
||||
@@ -24,7 +24,31 @@ namespace AibisDream.SystemEditor.Tests
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void PresentationSections_RoundTripAndLegacySnapshotsRemainReadable()
|
||||
public void PlayToolSection_RoundTripsThroughSnapshotJson()
|
||||
{
|
||||
var source = new SaveSnapshot();
|
||||
source.sections[SnapshotProviderIds.PlayTool] = new PlayToolSnapshotDto
|
||||
{
|
||||
isObjVisible = true,
|
||||
objPicName = "证物",
|
||||
isFullScreenVisible = true,
|
||||
fullScreenPicName = "教室"
|
||||
};
|
||||
|
||||
var json = JsonConvert.SerializeObject(source);
|
||||
var restored = JsonConvert.DeserializeObject<SaveSnapshot>(json);
|
||||
var playTool = ((JObject)restored.sections[SnapshotProviderIds.PlayTool])
|
||||
.ToObject<PlayToolSnapshotDto>();
|
||||
|
||||
Assert.That(playTool.isObjVisible, Is.True);
|
||||
Assert.That(playTool.objPicName, Is.EqualTo("证物"));
|
||||
Assert.That(playTool.isFullScreenVisible, Is.True);
|
||||
Assert.That(playTool.fullScreenPicName, Is.EqualTo("教室"));
|
||||
Assert.That(restored.schemaVersion, Is.EqualTo(SaveSnapshotSchema.CurrentVersion));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void ShowcaseAndDay2SleepSections_RoundTripSemanticState()
|
||||
{
|
||||
var source = new SaveSnapshot();
|
||||
source.sections[SnapshotProviderIds.Showcase] = new ShowcaseSnapshotDto
|
||||
@@ -33,40 +57,42 @@ namespace AibisDream.SystemEditor.Tests
|
||||
picName = "D2S星图",
|
||||
isBackgroundVisible = true
|
||||
};
|
||||
source.sections[SnapshotProviderIds.PlayTool] = new PlayToolSnapshotDto
|
||||
source.sections[SnapshotProviderIds.Day2SleepPresentation] =
|
||||
new Day2SleepPresentationSnapshotDto
|
||||
{
|
||||
isObjVisible = true,
|
||||
objPicName = "证物",
|
||||
objIsLocalized = true,
|
||||
isFullScreenVisible = true,
|
||||
fullScreenPicName = "病历",
|
||||
fullScreenIsLocalized = true
|
||||
mode = Day2SleepPresentationMode.LargeEffects.ToString(),
|
||||
largeBlurAmount = 0.72f,
|
||||
largeBlurSize = 0.015f,
|
||||
largeScaleMultiplier = 1.1f,
|
||||
largeAlpha = 0.4f
|
||||
};
|
||||
|
||||
var restored = JsonConvert.DeserializeObject<SaveSnapshot>(
|
||||
JsonConvert.SerializeObject(source));
|
||||
var json = JsonConvert.SerializeObject(source);
|
||||
var restored = JsonConvert.DeserializeObject<SaveSnapshot>(json);
|
||||
var showcase = ((JObject)restored.sections[SnapshotProviderIds.Showcase])
|
||||
.ToObject<ShowcaseSnapshotDto>();
|
||||
var playTool = ((JObject)restored.sections[SnapshotProviderIds.PlayTool])
|
||||
.ToObject<PlayToolSnapshotDto>();
|
||||
var day2Sleep = ((JObject)restored.sections[SnapshotProviderIds.Day2SleepPresentation])
|
||||
.ToObject<Day2SleepPresentationSnapshotDto>();
|
||||
|
||||
Assert.That(restored.schemaVersion, Is.EqualTo(SaveSnapshotSchema.CurrentVersion));
|
||||
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);
|
||||
Assert.That(showcase.isBackgroundVisible, Is.True);
|
||||
Assert.That(day2Sleep.mode, Is.EqualTo("LargeEffects"));
|
||||
Assert.That(day2Sleep.largeBlurAmount, Is.EqualTo(0.72f));
|
||||
Assert.That(day2Sleep.largeScaleMultiplier, Is.EqualTo(1.1f));
|
||||
Assert.That(day2Sleep.largeAlpha, Is.EqualTo(0.4f));
|
||||
}
|
||||
|
||||
var legacyPlayTool = JsonConvert.DeserializeObject<PlayToolSnapshotDto>(
|
||||
"{\"isObjVisible\":true,\"objPicName\":\"legacy\"}");
|
||||
Assert.That(legacyPlayTool.objIsLocalized, Is.False);
|
||||
Assert.That(legacyPlayTool.fullScreenIsLocalized, Is.False);
|
||||
|
||||
var legacy = JsonConvert.DeserializeObject<SaveSnapshot>(
|
||||
[Test]
|
||||
public void OldSnapshotWithoutPresentationSections_DeserializesWithoutSynthesizingSections()
|
||||
{
|
||||
var restored = JsonConvert.DeserializeObject<SaveSnapshot>(
|
||||
"{\"schemaVersion\":1,\"sections\":{}}");
|
||||
Assert.That(legacy.schemaVersion, Is.EqualTo(1));
|
||||
Assert.That(legacy.sections, Is.Empty);
|
||||
|
||||
Assert.That(restored.schemaVersion, Is.EqualTo(1));
|
||||
Assert.That(restored.sections.ContainsKey(SnapshotProviderIds.Showcase), Is.False);
|
||||
Assert.That(restored.sections.ContainsKey(SnapshotProviderIds.Day2SleepPresentation), Is.False);
|
||||
Assert.That(restored.sections.ContainsKey(SnapshotProviderIds.PlayTool), Is.False);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,8 +0,0 @@
|
||||
fileFormatVersion: 2
|
||||
guid: d6e75cb49ad84a8ebe0520a529e7a67e
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -1,50 +0,0 @@
|
||||
using AibisDream.FrameAnimation;
|
||||
using AibisDream.Utility;
|
||||
using NUnit.Framework;
|
||||
using UnityEditor;
|
||||
using UnityEditor.AddressableAssets;
|
||||
using UnityEngine;
|
||||
|
||||
namespace AibisDream.SystemEditor.Tests
|
||||
{
|
||||
public sealed class ActorPrefabContractTests
|
||||
{
|
||||
private const string PrefabPath =
|
||||
"Assets/Prefabs/FixSystemPrefabs/FrameAnimationActor.prefab";
|
||||
|
||||
[Test]
|
||||
public void FrameAnimationActor_PrefabAndSnapshotContractAreConsistent()
|
||||
{
|
||||
var prefab = AssetDatabase.LoadAssetAtPath<GameObject>(PrefabPath);
|
||||
Assert.That(prefab, Is.Not.Null);
|
||||
Assert.That(prefab.GetComponent<SpriteRenderer>(), Is.Not.Null);
|
||||
Assert.That(prefab.GetComponent<FrameAnimationPlayer>(), Is.Not.Null);
|
||||
Assert.That(prefab.GetComponent<FrameAnimationActor>(), Is.Not.Null);
|
||||
|
||||
var entry = AddressableAssetSettingsDefaultObject.Settings.FindAssetEntry(
|
||||
AssetDatabase.AssetPathToGUID(PrefabPath));
|
||||
Assert.That(entry, Is.Not.Null);
|
||||
Assert.That(entry.address, Is.EqualTo(ConstRef.FrameAnimationActorPrefabName));
|
||||
|
||||
var actorObject = Object.Instantiate(prefab);
|
||||
var slotObject = new GameObject("Actor Slot");
|
||||
try
|
||||
{
|
||||
var slot = slotObject.AddComponent<ActorSlot>();
|
||||
slot.slotName = "test-slot";
|
||||
var actor = actorObject.GetComponent<FrameAnimationActor>();
|
||||
actor.Init("TestActor", slot, ActorType.FrameAnimation);
|
||||
var snapshot = actor.CaptureEntry();
|
||||
|
||||
Assert.That(snapshot.actorName, Is.EqualTo("TestActor"));
|
||||
Assert.That(snapshot.slotName, Is.EqualTo("test-slot"));
|
||||
Assert.That(snapshot.actorType, Is.EqualTo(nameof(ActorType.FrameAnimation)));
|
||||
}
|
||||
finally
|
||||
{
|
||||
Object.DestroyImmediate(actorObject);
|
||||
Object.DestroyImmediate(slotObject);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,11 +0,0 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 23aa0707ef5c4618b55c455f2a03a612
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -1,152 +0,0 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using AibisDream.Kit;
|
||||
using AibisDream.SaveSystem;
|
||||
using FMOD;
|
||||
using FMOD.Studio;
|
||||
using Newtonsoft.Json;
|
||||
using Newtonsoft.Json.Linq;
|
||||
using NUnit.Framework;
|
||||
|
||||
namespace AibisDream.SystemEditor.Tests
|
||||
{
|
||||
public sealed class AudioSnapshotContractTests
|
||||
{
|
||||
[Test]
|
||||
public void Registry_CapturesFinalValuesOnlyForSuccessfulParameters()
|
||||
{
|
||||
var api = new FakeFmodGlobalParameterApi();
|
||||
api.Reads["stage"] = new ParameterRead(9f, 2.37f, RESULT.OK);
|
||||
api.SetResults["missing"] = RESULT.ERR_EVENT_NOTFOUND;
|
||||
var registry = new YarnGlobalParameterRegistry(api);
|
||||
|
||||
registry.SetFromYarn("stage", 9f);
|
||||
registry.SetFromYarn("STAGE", 10f);
|
||||
registry.SetFromYarn("missing", 1f);
|
||||
var snapshot = registry.CaptureFinalValues(_ => { });
|
||||
|
||||
Assert.That(registry.TrackedNames, Is.EqualTo(new[] { "stage" }));
|
||||
Assert.That(snapshot["stage"], Is.EqualTo(2.37f));
|
||||
Assert.That(api.SetCalls.All(call => !call.IgnoreSeekSpeed), Is.True);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void Registry_RestoreJumpsImmediatelyThenYarnUsesSeekSpeed()
|
||||
{
|
||||
var api = new FakeFmodGlobalParameterApi();
|
||||
var registry = new YarnGlobalParameterRegistry(api);
|
||||
|
||||
registry.RestoreFinalValues(
|
||||
new Dictionary<string, float> { ["stage"] = 2.37f },
|
||||
_ => { });
|
||||
registry.SetFromYarn("stage", 3f);
|
||||
|
||||
Assert.That(api.SetCalls, Has.Count.EqualTo(2));
|
||||
Assert.That(api.SetCalls[0].Value, Is.EqualTo(2.37f));
|
||||
Assert.That(api.SetCalls[0].IgnoreSeekSpeed, Is.True);
|
||||
Assert.That(api.SetCalls[1].Value, Is.EqualTo(3f));
|
||||
Assert.That(api.SetCalls[1].IgnoreSeekSpeed, Is.False);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void AudioSnapshot_RoundTripsWithoutChangingSchema()
|
||||
{
|
||||
var source = new SaveSnapshot();
|
||||
source.sections[SnapshotProviderIds.Audio] = new AudioSnapshotDto
|
||||
{
|
||||
ambState = "Dream",
|
||||
yarnGlobalParameters = new Dictionary<string, float>
|
||||
{
|
||||
["reverb"] = 0.42f,
|
||||
["hs1LogStage"] = 2.37f
|
||||
}
|
||||
};
|
||||
|
||||
var restored = JsonConvert.DeserializeObject<SaveSnapshot>(
|
||||
JsonConvert.SerializeObject(source));
|
||||
var audio = ((JObject)restored.sections[SnapshotProviderIds.Audio])
|
||||
.ToObject<AudioSnapshotDto>();
|
||||
|
||||
Assert.That(restored.schemaVersion, Is.EqualTo(SaveSnapshotSchema.CurrentVersion));
|
||||
Assert.That(audio.yarnGlobalParameters["reverb"], Is.EqualTo(0.42f));
|
||||
Assert.That(audio.yarnGlobalParameters["hs1LogStage"], Is.EqualTo(2.37f));
|
||||
}
|
||||
|
||||
private readonly struct SetCall
|
||||
{
|
||||
internal SetCall(float value, bool ignoreSeekSpeed)
|
||||
{
|
||||
Value = value;
|
||||
IgnoreSeekSpeed = ignoreSeekSpeed;
|
||||
}
|
||||
|
||||
internal float Value { get; }
|
||||
internal bool IgnoreSeekSpeed { get; }
|
||||
}
|
||||
|
||||
private readonly struct ParameterRead
|
||||
{
|
||||
internal ParameterRead(float value, float finalValue, RESULT result)
|
||||
{
|
||||
Value = value;
|
||||
FinalValue = finalValue;
|
||||
Result = result;
|
||||
}
|
||||
|
||||
internal float Value { get; }
|
||||
internal float FinalValue { get; }
|
||||
internal RESULT Result { get; }
|
||||
}
|
||||
|
||||
private sealed class FakeFmodGlobalParameterApi : IFmodGlobalParameterApi
|
||||
{
|
||||
internal readonly Dictionary<string, RESULT> SetResults =
|
||||
new Dictionary<string, RESULT>(System.StringComparer.OrdinalIgnoreCase);
|
||||
internal readonly Dictionary<string, ParameterRead> Reads =
|
||||
new Dictionary<string, ParameterRead>(System.StringComparer.OrdinalIgnoreCase);
|
||||
internal readonly List<SetCall> SetCalls = new List<SetCall>();
|
||||
|
||||
public RESULT SetParameterByName(
|
||||
string name,
|
||||
float value,
|
||||
bool ignoreSeekSpeed)
|
||||
{
|
||||
SetCalls.Add(new SetCall(value, ignoreSeekSpeed));
|
||||
return SetResults.TryGetValue(name, out var result) ? result : RESULT.OK;
|
||||
}
|
||||
|
||||
public RESULT GetParameterByName(
|
||||
string name,
|
||||
out float value,
|
||||
out float finalValue)
|
||||
{
|
||||
if (Reads.TryGetValue(name, out var read))
|
||||
{
|
||||
value = read.Value;
|
||||
finalValue = read.FinalValue;
|
||||
return read.Result;
|
||||
}
|
||||
|
||||
value = 0f;
|
||||
finalValue = 0f;
|
||||
return RESULT.ERR_EVENT_NOTFOUND;
|
||||
}
|
||||
|
||||
public RESULT GetParameterDescriptionByName(
|
||||
string name,
|
||||
out PARAMETER_DESCRIPTION description)
|
||||
{
|
||||
description = default;
|
||||
return RESULT.ERR_EVENT_NOTFOUND;
|
||||
}
|
||||
|
||||
public RESULT SetParameterById(
|
||||
PARAMETER_ID id,
|
||||
float value,
|
||||
bool ignoreSeekSpeed)
|
||||
{
|
||||
return RESULT.OK;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,131 +0,0 @@
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using AibisDream.Framework;
|
||||
using AibisDream.Kit;
|
||||
using AibisDream.MiniGame.Language;
|
||||
using AibisDream.Utility;
|
||||
using NUnit.Framework;
|
||||
using UnityEditor;
|
||||
using UnityEngine;
|
||||
using UnityEngine.Localization;
|
||||
|
||||
namespace AibisDream.SystemEditor.Tests
|
||||
{
|
||||
public sealed class LocalizationContractTests
|
||||
{
|
||||
private const string ParticleProfilePath =
|
||||
"Assets/GameContent/Feature_Huoshan/Expression/ExpressionParticleLanguageProfile.asset";
|
||||
|
||||
[Test]
|
||||
public void LocaleMappings_ResolveSupportedCodesAndSystemLanguages()
|
||||
{
|
||||
var localeCases = new Dictionary<string, LocaleKind>
|
||||
{
|
||||
["ru"] = LocaleKind.Ru,
|
||||
["ru-RU"] = LocaleKind.Ru,
|
||||
["es"] = LocaleKind.Es,
|
||||
["es-ES"] = LocaleKind.Es,
|
||||
["pt"] = LocaleKind.PtBr,
|
||||
["pt-BR"] = LocaleKind.PtBr
|
||||
};
|
||||
foreach (var pair in localeCases)
|
||||
Assert.That(LocalizationKit.GetLocaleKind(pair.Key), Is.EqualTo(pair.Value), pair.Key);
|
||||
|
||||
Assert.That(LocalizationKit.MapSystemLanguage(SystemLanguage.Russian), Is.EqualTo("ru"));
|
||||
Assert.That(LocalizationKit.MapSystemLanguage(SystemLanguage.Spanish), Is.EqualTo("es"));
|
||||
Assert.That(LocalizationKit.MapSystemLanguage(SystemLanguage.Portuguese), Is.EqualTo("pt-BR"));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void UnknownCharacter_UsesItsKeyAsEveryLocalizedName()
|
||||
{
|
||||
var method = typeof(YarnUtil).GetMethod(
|
||||
"GetCharacterVoByKey",
|
||||
BindingFlags.Static | BindingFlags.NonPublic);
|
||||
Assert.That(method, Is.Not.Null);
|
||||
|
||||
const string missingKey = "missing_character_contract_test";
|
||||
var character = (CharacterVo)method.Invoke(null, new object[] { missingKey });
|
||||
|
||||
Assert.That(character.key, Is.EqualTo(missingKey));
|
||||
Assert.That(
|
||||
new[]
|
||||
{
|
||||
character.cn,
|
||||
character.en,
|
||||
character.ja,
|
||||
character.ru,
|
||||
character.es,
|
||||
character.ptBr
|
||||
},
|
||||
Has.All.EqualTo(missingKey));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void CharacterCsv_ParsesExpandedLocaleColumns()
|
||||
{
|
||||
var characters = CsvUtil.ReadAsBean<Character>(Path.Combine(
|
||||
Application.streamingAssetsPath,
|
||||
"Config",
|
||||
"character.csv"));
|
||||
|
||||
Assert.That(characters, Is.Not.Empty);
|
||||
var peipei = characters.Single(character => character.Key == "peipei");
|
||||
Assert.That(peipei.Ru, Is.Not.Null);
|
||||
Assert.That(peipei.Es, Is.Not.Null);
|
||||
Assert.That(peipei.PtBr, Is.Not.Null);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void ExpressionParsing_PreservesUnicodeAndWordBoundaries()
|
||||
{
|
||||
const string combining = "e\u0301";
|
||||
Assert.That(
|
||||
LanguageYarnCommand.TryParseExpressionPool(
|
||||
$"中 中 あ {combining} 😀。",
|
||||
out List<string> graphemePool),
|
||||
Is.True);
|
||||
Assert.That(
|
||||
graphemePool,
|
||||
Is.EqualTo(new[] { "中", "中", "あ", combining, "😀", "。" }));
|
||||
|
||||
Assert.That(
|
||||
ExpressionTextTokenizer.TokenizeText(
|
||||
"don't self-doubt ¿Por qué? веришь?",
|
||||
ExpressionParticleUnitMode.Word),
|
||||
Is.EqualTo(new[]
|
||||
{
|
||||
"don't",
|
||||
"self-doubt",
|
||||
"¿Por",
|
||||
"qué?",
|
||||
"веришь?"
|
||||
}));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void ExpressionParticleProfile_MapsLanguageFamilies()
|
||||
{
|
||||
var profile = AssetDatabase.LoadAssetAtPath<ExpressionParticleLanguageProfile>(
|
||||
ParticleProfilePath);
|
||||
Assert.That(profile, Is.Not.Null);
|
||||
Assert.That(profile.GetValidationErrors(), Is.Empty);
|
||||
|
||||
Assert.That(
|
||||
profile.Resolve(new LocaleIdentifier("zh-Hans")).UnitMode,
|
||||
Is.EqualTo(ExpressionParticleUnitMode.Grapheme));
|
||||
Assert.That(
|
||||
profile.Resolve(new LocaleIdentifier("ja-JP")).UnitMode,
|
||||
Is.EqualTo(ExpressionParticleUnitMode.Grapheme));
|
||||
foreach (var localeCode in new[] { "en", "es", "ru", "pt-BR" })
|
||||
{
|
||||
Assert.That(
|
||||
profile.Resolve(new LocaleIdentifier(localeCode)).UnitMode,
|
||||
Is.EqualTo(ExpressionParticleUnitMode.Word),
|
||||
localeCode);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,11 +0,0 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 7f556352cf7f71c4a98e967b54dffc3b
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -1,94 +0,0 @@
|
||||
using NUnit.Framework;
|
||||
|
||||
namespace AibisDream.SystemEditor.Tests
|
||||
{
|
||||
public sealed class TextSpeedSettingsTests
|
||||
{
|
||||
[SetUp]
|
||||
public void SetUp()
|
||||
{
|
||||
TextSpeedSettings.Reset();
|
||||
}
|
||||
|
||||
[TearDown]
|
||||
public void TearDown()
|
||||
{
|
||||
TextSpeedSettings.Reset();
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void Normalize_MapsEverySupportedValueToCanonicalForm()
|
||||
{
|
||||
AssertNormalized(
|
||||
"0.5",
|
||||
TextSpeedSettings.SlowMultiplier,
|
||||
TextSpeedSettings.SlowValue);
|
||||
AssertNormalized(
|
||||
"1.0",
|
||||
TextSpeedSettings.DefaultMultiplier,
|
||||
TextSpeedSettings.DefaultValue);
|
||||
AssertNormalized(
|
||||
"3",
|
||||
TextSpeedSettings.FastMultiplier,
|
||||
TextSpeedSettings.FastValue);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void Normalize_RejectsInvalidAndUnsupportedValues()
|
||||
{
|
||||
foreach (var rawValue in new[]
|
||||
{
|
||||
null,
|
||||
string.Empty,
|
||||
"invalid",
|
||||
"NaN",
|
||||
"Infinity",
|
||||
"0",
|
||||
"2",
|
||||
"4"
|
||||
})
|
||||
{
|
||||
Assert.That(
|
||||
TextSpeedSettings.TryNormalize(
|
||||
rawValue,
|
||||
out var multiplier,
|
||||
out var canonicalValue),
|
||||
Is.False,
|
||||
rawValue);
|
||||
Assert.That(multiplier, Is.EqualTo(TextSpeedSettings.DefaultMultiplier));
|
||||
Assert.That(canonicalValue, Is.EqualTo(TextSpeedSettings.DefaultValue));
|
||||
}
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void Reset_RestoresDefaultAfterRuntimeChange()
|
||||
{
|
||||
Assert.That(TextSpeedSettings.ApplyStoredValue("3"), Is.True);
|
||||
|
||||
TextSpeedSettings.Reset();
|
||||
|
||||
Assert.That(
|
||||
TextSpeedSettings.CurrentMultiplier,
|
||||
Is.EqualTo(TextSpeedSettings.DefaultMultiplier));
|
||||
Assert.That(
|
||||
TextSpeedSettings.CurrentCanonicalValue,
|
||||
Is.EqualTo(TextSpeedSettings.DefaultValue));
|
||||
}
|
||||
|
||||
private static void AssertNormalized(
|
||||
string rawValue,
|
||||
float expectedMultiplier,
|
||||
string expectedCanonicalValue)
|
||||
{
|
||||
Assert.That(
|
||||
TextSpeedSettings.TryNormalize(
|
||||
rawValue,
|
||||
out var multiplier,
|
||||
out var canonicalValue),
|
||||
Is.True,
|
||||
rawValue);
|
||||
Assert.That(multiplier, Is.EqualTo(expectedMultiplier));
|
||||
Assert.That(canonicalValue, Is.EqualTo(expectedCanonicalValue));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,182 +0,0 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using AibisDream.Framework;
|
||||
using AibisDream.EditorTools;
|
||||
using AibisDream.UI;
|
||||
using NUnit.Framework;
|
||||
using TMPro;
|
||||
using UnityEditor;
|
||||
using UnityEditor.AddressableAssets;
|
||||
using UnityEditor.SceneManagement;
|
||||
using UnityEngine;
|
||||
using UnityEngine.SceneManagement;
|
||||
|
||||
namespace AibisDream.SystemEditor.Tests
|
||||
{
|
||||
public sealed class TypographyContractTests
|
||||
{
|
||||
private const string ProfileFolder = "Assets/GameContent/Common/Typography/Profiles";
|
||||
|
||||
[TestCase(LocaleKind.Cn, TypographyProfileKind.ZhHans)]
|
||||
[TestCase(LocaleKind.Ja, TypographyProfileKind.Japanese)]
|
||||
[TestCase(LocaleKind.En, TypographyProfileKind.Latin)]
|
||||
[TestCase(LocaleKind.Es, TypographyProfileKind.Latin)]
|
||||
[TestCase(LocaleKind.PtBr, TypographyProfileKind.Latin)]
|
||||
[TestCase(LocaleKind.Ru, TypographyProfileKind.Russian)]
|
||||
public void LocaleMapping_UsesExpectedProfile(LocaleKind locale, TypographyProfileKind expected)
|
||||
{
|
||||
Assert.That(TypographyLocaleMapping.GetProfileKind(locale), Is.EqualTo(expected));
|
||||
Assert.That(
|
||||
TypographyLocaleMapping.GetProfileKey(locale),
|
||||
Is.EqualTo($"Typography/Profile/{expected}"));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void Resolve_MissingRoleFallsBackToBody()
|
||||
{
|
||||
TMP_FontAsset font = AssetDatabase.LoadAssetAtPath<TMP_FontAsset>(
|
||||
"Assets/Font/Assets/ChillBitmap_16px SDF.asset");
|
||||
var profile = ScriptableObject.CreateInstance<TypographyProfile>();
|
||||
try
|
||||
{
|
||||
var body = new TypographyStyle(TypographyRole.Body, font);
|
||||
profile.Configure(TypographyProfileKind.Latin, new[] { body });
|
||||
Assert.That(profile.Resolve(TypographyRole.Dialogue), Is.SameAs(body));
|
||||
}
|
||||
finally
|
||||
{
|
||||
UnityEngine.Object.DestroyImmediate(profile);
|
||||
}
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void Binding_ReapplyUsesCapturedBaselineWithoutAccumulatingMetrics()
|
||||
{
|
||||
TMP_FontAsset font = AssetDatabase.LoadAssetAtPath<TMP_FontAsset>(
|
||||
"Assets/Font/Assets/ChillBitmap_16px SDF.asset");
|
||||
var profile = ScriptableObject.CreateInstance<TypographyProfile>();
|
||||
var go = new GameObject("TypographyMetricContract", typeof(RectTransform));
|
||||
try
|
||||
{
|
||||
TextMeshProUGUI text = go.AddComponent<TextMeshProUGUI>();
|
||||
text.fontSize = 40f;
|
||||
text.lineSpacing = 3f;
|
||||
text.characterSpacing = 2f;
|
||||
LocalizedTypography binding = go.AddComponent<LocalizedTypography>();
|
||||
profile.Configure(
|
||||
TypographyProfileKind.Latin,
|
||||
new[]
|
||||
{
|
||||
new TypographyStyle(
|
||||
TypographyRole.Body,
|
||||
font,
|
||||
fontSizeMultiplier: 0.75f,
|
||||
lineSpacingOffset: 1f,
|
||||
characterSpacingOffset: 2f)
|
||||
});
|
||||
|
||||
binding.ApplyProfile(profile);
|
||||
binding.ApplyProfile(profile);
|
||||
|
||||
Assert.That(text.fontSize, Is.EqualTo(30f));
|
||||
Assert.That(text.lineSpacing, Is.EqualTo(4f));
|
||||
Assert.That(text.characterSpacing, Is.EqualTo(4f));
|
||||
}
|
||||
finally
|
||||
{
|
||||
UnityEngine.Object.DestroyImmediate(go);
|
||||
UnityEngine.Object.DestroyImmediate(profile);
|
||||
}
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void FirstRoundProfiles_AreCompleteAndAddressableByConvention()
|
||||
{
|
||||
foreach (TypographyProfileKind kind in Enum.GetValues(typeof(TypographyProfileKind)))
|
||||
{
|
||||
TypographyProfile profile = AssetDatabase.LoadAssetAtPath<TypographyProfile>(
|
||||
$"{ProfileFolder}/{kind}.asset");
|
||||
Assert.That(profile, Is.Not.Null, kind.ToString());
|
||||
Assert.That(profile.ProfileKind, Is.EqualTo(kind));
|
||||
Assert.That(profile.GetValidationErrors(), Is.Empty, kind.ToString());
|
||||
Assert.That(
|
||||
Enum.GetValues(typeof(TypographyRole)).Cast<TypographyRole>()
|
||||
.All(role => profile.Resolve(role) != null),
|
||||
Is.True,
|
||||
kind.ToString());
|
||||
|
||||
string guid = AssetDatabase.AssetPathToGUID($"{ProfileFolder}/{kind}.asset");
|
||||
var entry = AddressableAssetSettingsDefaultObject.Settings.FindAssetEntry(guid);
|
||||
Assert.That(entry, Is.Not.Null, kind.ToString());
|
||||
Assert.That(entry.address, Is.EqualTo($"Typography/Profile/{kind}"));
|
||||
}
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TerminalPrefabs_BindEveryTextExactlyOnce()
|
||||
{
|
||||
foreach (string path in TerminalTypographyMigration.PrefabPaths)
|
||||
{
|
||||
GameObject prefab = AssetDatabase.LoadAssetAtPath<GameObject>(path);
|
||||
Assert.That(prefab, Is.Not.Null, path);
|
||||
|
||||
TMP_Text[] texts = prefab.GetComponentsInChildren<TMP_Text>(true);
|
||||
Assert.That(texts, Is.Not.Empty, path);
|
||||
foreach (TMP_Text text in texts)
|
||||
{
|
||||
LocalizedTypography[] bindings = text.GetComponents<LocalizedTypography>();
|
||||
Assert.That(bindings, Has.Length.EqualTo(1), $"{path}: {text.name}");
|
||||
Assert.That(bindings[0].Role, Is.EqualTo(TypographyRole.Terminal),
|
||||
$"{path}: {text.name}");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void PersistenceTerminalComposition_InheritsTypographyBindings()
|
||||
{
|
||||
Scene scene = SceneManager.GetSceneByPath(TerminalTypographyMigration.PersistenceScenePath);
|
||||
bool openedForTest = !scene.IsValid() || !scene.isLoaded;
|
||||
if (openedForTest)
|
||||
{
|
||||
scene = EditorSceneManager.OpenScene(
|
||||
TerminalTypographyMigration.PersistenceScenePath,
|
||||
OpenSceneMode.Additive);
|
||||
}
|
||||
try
|
||||
{
|
||||
Component[] hosts = scene.GetRootGameObjects()
|
||||
.SelectMany(root => root.GetComponentsInChildren<Component>(true))
|
||||
.Where(component => component is TerminalPanel || component is InGameTerminalPanel)
|
||||
.ToArray();
|
||||
Assert.That(hosts, Has.Length.EqualTo(2));
|
||||
|
||||
var violations = new List<string>();
|
||||
foreach (Component host in hosts)
|
||||
{
|
||||
foreach (TMP_Text text in host.GetComponentsInChildren<TMP_Text>(true))
|
||||
{
|
||||
LocalizedTypography[] bindings = text.GetComponents<LocalizedTypography>();
|
||||
if (bindings.Length != 1)
|
||||
{
|
||||
violations.Add($"{host.name}: {text.name} has {bindings.Length} bindings");
|
||||
continue;
|
||||
}
|
||||
|
||||
if (bindings[0].Role != TypographyRole.Terminal)
|
||||
violations.Add($"{host.name}: {text.name} uses {bindings[0].Role}");
|
||||
}
|
||||
}
|
||||
|
||||
Assert.That(violations, Is.Empty, string.Join("\n", violations));
|
||||
}
|
||||
finally
|
||||
{
|
||||
if (openedForTest)
|
||||
EditorSceneManager.CloseScene(scene, true);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -1,75 +0,0 @@
|
||||
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 YarnLineMetadataTests
|
||||
{
|
||||
[Test]
|
||||
public void AutoNext_UsesDefaultOrInvariantParameterizedDelay()
|
||||
{
|
||||
var plain = LineInfo.Generate(CreateLine("auto_next"));
|
||||
Assert.That(plain.isAutoSkip, Is.True);
|
||||
Assert.That(plain.CalcAutoNextDelayTime(), Is.EqualTo(ConstRef.FixedDelay));
|
||||
|
||||
var originalCulture = CultureInfo.CurrentCulture;
|
||||
try
|
||||
{
|
||||
CultureInfo.CurrentCulture = new CultureInfo("de-DE");
|
||||
var parameterized = LineInfo.Generate(CreateLine("auto_next:4.5"));
|
||||
Assert.That(parameterized.isAutoSkip, Is.True);
|
||||
Assert.That(parameterized.autoNextDelaySeconds, Is.EqualTo(4.5f));
|
||||
Assert.That(parameterized.CalcAutoNextDelayTime(), Is.EqualTo(4500));
|
||||
}
|
||||
finally
|
||||
{
|
||||
CultureInfo.CurrentCulture = originalCulture;
|
||||
}
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void AutoNext_InvalidValueFallsBackAndSimilarTagIsIgnored()
|
||||
{
|
||||
LogAssert.Expect(LogType.Warning, new Regex("auto_next 参数无效"));
|
||||
var invalid = LineInfo.Generate(CreateLine("auto_next:abc"));
|
||||
|
||||
Assert.That(invalid.isAutoSkip, Is.True);
|
||||
Assert.That(invalid.autoNextDelaySeconds, Is.Null);
|
||||
Assert.That(invalid.CalcAutoNextDelayTime(), Is.EqualTo(ConstRef.FixedDelay));
|
||||
|
||||
var similar = CreateLine("AUTO_NEXT", "auto_next_extra:4.5");
|
||||
Assert.That(similar.IsAutoSkipLine(), Is.False);
|
||||
Assert.That(similar.TryGetAutoNextDelaySeconds(out _), Is.False);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void OptionPrompt_RequiresExactMetadataTag()
|
||||
{
|
||||
Assert.That(CreateLine(YarnUtil.OptionPrompt).IsOptionPromptLine(), Is.True);
|
||||
Assert.That(CreateLine("OPTION_PROMPT").IsOptionPromptLine(), Is.False);
|
||||
Assert.That(CreateLine("option_prompts").IsOptionPromptLine(), Is.False);
|
||||
Assert.That(
|
||||
new LocalizedLine { Metadata = null }.IsOptionPromptLine(),
|
||||
Is.False);
|
||||
}
|
||||
|
||||
private static LocalizedLine CreateLine(params string[] metadata)
|
||||
{
|
||||
return new LocalizedLine
|
||||
{
|
||||
TextID = "line:yarn-metadata-contract",
|
||||
Metadata = metadata,
|
||||
Text = new MarkupParseResult(
|
||||
"Test line",
|
||||
new List<MarkupAttribute>())
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,11 +0,0 @@
|
||||
fileFormatVersion: 2
|
||||
guid: a9b456d04f99d9849a0200e688ddfb39
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,75 @@
|
||||
using NUnit.Framework;
|
||||
|
||||
namespace AibisDream.SystemEditor.Tests
|
||||
{
|
||||
public sealed class TextSpeedSettingsTests
|
||||
{
|
||||
[SetUp]
|
||||
public void SetUp()
|
||||
{
|
||||
TextSpeedSettings.Reset();
|
||||
}
|
||||
|
||||
[TearDown]
|
||||
public void TearDown()
|
||||
{
|
||||
TextSpeedSettings.Reset();
|
||||
}
|
||||
|
||||
[TestCase("0.5", TextSpeedSettings.SlowMultiplier, TextSpeedSettings.SlowValue)]
|
||||
[TestCase("0.50", TextSpeedSettings.SlowMultiplier, TextSpeedSettings.SlowValue)]
|
||||
[TestCase("1", TextSpeedSettings.DefaultMultiplier, TextSpeedSettings.DefaultValue)]
|
||||
[TestCase("1.0", TextSpeedSettings.DefaultMultiplier, TextSpeedSettings.DefaultValue)]
|
||||
[TestCase("3", TextSpeedSettings.FastMultiplier, TextSpeedSettings.FastValue)]
|
||||
[TestCase("3.0", TextSpeedSettings.FastMultiplier, TextSpeedSettings.FastValue)]
|
||||
public void TryNormalize_SupportedNumericValue_ReturnsCanonicalValue(
|
||||
string rawValue,
|
||||
float expectedMultiplier,
|
||||
string expectedCanonicalValue)
|
||||
{
|
||||
var result = TextSpeedSettings.TryNormalize(
|
||||
rawValue,
|
||||
out var multiplier,
|
||||
out var canonicalValue);
|
||||
|
||||
Assert.That(result, Is.True);
|
||||
Assert.That(multiplier, Is.EqualTo(expectedMultiplier));
|
||||
Assert.That(canonicalValue, Is.EqualTo(expectedCanonicalValue));
|
||||
}
|
||||
|
||||
[TestCase(null)]
|
||||
[TestCase("")]
|
||||
[TestCase(" ")]
|
||||
[TestCase("invalid")]
|
||||
[TestCase("NaN")]
|
||||
[TestCase("Infinity")]
|
||||
[TestCase("-1")]
|
||||
[TestCase("0")]
|
||||
[TestCase("2")]
|
||||
[TestCase("4")]
|
||||
public void TryNormalize_InvalidOrUnsupportedValue_ReturnsDefault(
|
||||
string rawValue)
|
||||
{
|
||||
var result = TextSpeedSettings.TryNormalize(
|
||||
rawValue,
|
||||
out var multiplier,
|
||||
out var canonicalValue);
|
||||
|
||||
Assert.That(result, Is.False);
|
||||
Assert.That(multiplier, Is.EqualTo(TextSpeedSettings.DefaultMultiplier));
|
||||
Assert.That(canonicalValue, Is.EqualTo(TextSpeedSettings.DefaultValue));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void Reset_AfterApplyingFastValue_RestoresDefault()
|
||||
{
|
||||
Assert.That(TextSpeedSettings.ApplyStoredValue("3"), Is.True);
|
||||
Assert.That(TextSpeedSettings.CurrentMultiplier, Is.EqualTo(TextSpeedSettings.FastMultiplier));
|
||||
|
||||
TextSpeedSettings.Reset();
|
||||
|
||||
Assert.That(TextSpeedSettings.CurrentMultiplier, Is.EqualTo(TextSpeedSettings.DefaultMultiplier));
|
||||
Assert.That(TextSpeedSettings.CurrentCanonicalValue, Is.EqualTo(TextSpeedSettings.DefaultValue));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,229 +0,0 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using AibisDream.Framework;
|
||||
using AibisDream.UI;
|
||||
using TMPro;
|
||||
using UnityEditor;
|
||||
using UnityEditor.SceneManagement;
|
||||
using UnityEngine;
|
||||
using UnityEngine.SceneManagement;
|
||||
|
||||
namespace AibisDream.EditorTools
|
||||
{
|
||||
public static class TerminalTypographyMigration
|
||||
{
|
||||
public const string PrefabFolder = "Assets/GameContent/Feature_MainUI/Prefabs";
|
||||
public const string PersistenceScenePath = "Assets/Scenes/Persistence.unity";
|
||||
|
||||
// Sources must be migrated before their consumers so existing added-component
|
||||
// overrides can be replaced by the component inherited from the source prefab.
|
||||
public static readonly IReadOnlyList<string> PrefabPaths = new[]
|
||||
{
|
||||
$"{PrefabFolder}/Common/Select Item.prefab",
|
||||
$"{PrefabFolder}/Common/Slider Item.prefab",
|
||||
$"{PrefabFolder}/Common/TerminalButton.prefab",
|
||||
$"{PrefabFolder}/Common/TerminalChapterCard.prefab",
|
||||
$"{PrefabFolder}/Common/TerminalHeader.prefab",
|
||||
$"{PrefabFolder}/Common/TerminalMenuItem.prefab",
|
||||
$"{PrefabFolder}/Common/TerminalOptionItem.prefab",
|
||||
$"{PrefabFolder}/Common/TerminalRecordItem.prefab",
|
||||
$"{PrefabFolder}/Common/TerminalSliderItem.prefab",
|
||||
$"{PrefabFolder}/Common/TerminalChapterPanel.prefab",
|
||||
$"{PrefabFolder}/TerminalChapterPanel.prefab",
|
||||
$"{PrefabFolder}/TerminalLoadingPanel.prefab",
|
||||
$"{PrefabFolder}/TerminalRecordPanel.prefab",
|
||||
$"{PrefabFolder}/TerminalSettingPanel.prefab",
|
||||
$"{PrefabFolder}/TerminalSettingPanelMini.prefab",
|
||||
$"{PrefabFolder}/TerminalStartPanel.prefab"
|
||||
};
|
||||
|
||||
[MenuItem("Tools/AIBIS Dream/Typography/Migrate Terminal Prefabs")]
|
||||
public static void MigrateTerminalPrefabs()
|
||||
{
|
||||
int textCount = 0;
|
||||
int addedCount = 0;
|
||||
int removedOverrideCount = 0;
|
||||
|
||||
foreach (string path in PrefabPaths)
|
||||
{
|
||||
MigrationResult result = MigratePrefab(path);
|
||||
textCount += result.TextCount;
|
||||
addedCount += result.AddedCount;
|
||||
removedOverrideCount += result.RemovedOverrideCount;
|
||||
}
|
||||
|
||||
int sceneBindingCount = MigratePersistenceScene();
|
||||
AssetDatabase.SaveAssets();
|
||||
Debug.Log(
|
||||
$"[TerminalTypographyMigration] Migrated {PrefabPaths.Count} prefabs and " +
|
||||
$"{textCount} TMP texts. Added {addedCount} bindings; removed " +
|
||||
$"{removedOverrideCount} redundant nested overrides; added " +
|
||||
$"{sceneBindingCount} scene-owned bindings.");
|
||||
}
|
||||
|
||||
public static void MigrateFromCommandLine()
|
||||
{
|
||||
try
|
||||
{
|
||||
MigrateTerminalPrefabs();
|
||||
}
|
||||
catch (Exception exception)
|
||||
{
|
||||
Debug.LogException(exception);
|
||||
throw;
|
||||
}
|
||||
}
|
||||
|
||||
private static MigrationResult MigratePrefab(string path)
|
||||
{
|
||||
GameObject root = PrefabUtility.LoadPrefabContents(path);
|
||||
try
|
||||
{
|
||||
TMP_Text[] texts = root.GetComponentsInChildren<TMP_Text>(true);
|
||||
if (texts.Length == 0)
|
||||
throw new InvalidOperationException($"Terminal prefab contains no TMP text: {path}");
|
||||
|
||||
int addedCount = 0;
|
||||
int removedOverrideCount = 0;
|
||||
foreach (TMP_Text text in texts)
|
||||
{
|
||||
int removedForText = RemoveRedundantNestedOverrides(text);
|
||||
removedOverrideCount += removedForText;
|
||||
|
||||
LocalizedTypography[] bindings = text.GetComponents<LocalizedTypography>();
|
||||
if (bindings.Length == 0)
|
||||
{
|
||||
// Saving the removed override makes the source component visible
|
||||
// again when this consumer prefab is next loaded.
|
||||
if (removedForText > 0)
|
||||
continue;
|
||||
|
||||
LocalizedTypography binding = text.gameObject.AddComponent<LocalizedTypography>();
|
||||
binding.ConfigureForEditor(TypographyRole.Terminal);
|
||||
addedCount++;
|
||||
}
|
||||
else if (bindings.Length == 1)
|
||||
{
|
||||
ConfigureIfOwnedByCurrentPrefab(bindings[0]);
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new InvalidOperationException(
|
||||
$"TMP text has multiple typography bindings after cleanup: {path} / {text.name}");
|
||||
}
|
||||
}
|
||||
|
||||
PrefabUtility.SaveAsPrefabAsset(root, path);
|
||||
return new MigrationResult(texts.Length, addedCount, removedOverrideCount);
|
||||
}
|
||||
finally
|
||||
{
|
||||
PrefabUtility.UnloadPrefabContents(root);
|
||||
}
|
||||
}
|
||||
|
||||
private static int MigratePersistenceScene()
|
||||
{
|
||||
Scene scene = SceneManager.GetSceneByPath(PersistenceScenePath);
|
||||
bool openedForMigration = !scene.IsValid() || !scene.isLoaded;
|
||||
if (openedForMigration)
|
||||
scene = EditorSceneManager.OpenScene(PersistenceScenePath, OpenSceneMode.Additive);
|
||||
try
|
||||
{
|
||||
int addedCount = 0;
|
||||
var visited = new HashSet<TMP_Text>();
|
||||
foreach (GameObject root in scene.GetRootGameObjects())
|
||||
{
|
||||
Component[] hosts = root.GetComponentsInChildren<Component>(true);
|
||||
foreach (Component host in hosts)
|
||||
{
|
||||
if (host is not TerminalPanel && host is not InGameTerminalPanel)
|
||||
continue;
|
||||
|
||||
foreach (TMP_Text text in host.GetComponentsInChildren<TMP_Text>(true))
|
||||
{
|
||||
if (!visited.Add(text))
|
||||
continue;
|
||||
|
||||
LocalizedTypography[] bindings = text.GetComponents<LocalizedTypography>();
|
||||
if (bindings.Length == 1)
|
||||
continue;
|
||||
if (bindings.Length > 1)
|
||||
{
|
||||
throw new InvalidOperationException(
|
||||
$"Scene TMP text has multiple typography bindings: {text.name}");
|
||||
}
|
||||
if (PrefabUtility.IsPartOfPrefabInstance(text))
|
||||
{
|
||||
throw new InvalidOperationException(
|
||||
$"Terminal prefab instance is missing its source binding: {text.name}");
|
||||
}
|
||||
|
||||
LocalizedTypography binding = text.gameObject.AddComponent<LocalizedTypography>();
|
||||
binding.ConfigureForEditor(TypographyRole.Terminal);
|
||||
addedCount++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (addedCount > 0)
|
||||
{
|
||||
EditorSceneManager.MarkSceneDirty(scene);
|
||||
EditorSceneManager.SaveScene(scene);
|
||||
}
|
||||
|
||||
return addedCount;
|
||||
}
|
||||
finally
|
||||
{
|
||||
if (openedForMigration)
|
||||
EditorSceneManager.CloseScene(scene, true);
|
||||
}
|
||||
}
|
||||
|
||||
private static int RemoveRedundantNestedOverrides(TMP_Text text)
|
||||
{
|
||||
GameObject sourceObject = PrefabUtility.GetCorrespondingObjectFromSource(text.gameObject);
|
||||
if (sourceObject == null || sourceObject.GetComponent<LocalizedTypography>() == null)
|
||||
return 0;
|
||||
|
||||
int removed = 0;
|
||||
LocalizedTypography[] bindings = text.GetComponents<LocalizedTypography>();
|
||||
foreach (LocalizedTypography binding in bindings)
|
||||
{
|
||||
if (!PrefabUtility.IsAddedComponentOverride(binding))
|
||||
continue;
|
||||
|
||||
UnityEngine.Object.DestroyImmediate(binding, true);
|
||||
removed++;
|
||||
}
|
||||
|
||||
return removed;
|
||||
}
|
||||
|
||||
private static void ConfigureIfOwnedByCurrentPrefab(LocalizedTypography binding)
|
||||
{
|
||||
if (PrefabUtility.IsPartOfPrefabInstance(binding) &&
|
||||
PrefabUtility.GetCorrespondingObjectFromSource(binding) != null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
binding.ConfigureForEditor(TypographyRole.Terminal);
|
||||
}
|
||||
|
||||
private readonly struct MigrationResult
|
||||
{
|
||||
public MigrationResult(int textCount, int addedCount, int removedOverrideCount)
|
||||
{
|
||||
TextCount = textCount;
|
||||
AddedCount = addedCount;
|
||||
RemovedOverrideCount = removedOverrideCount;
|
||||
}
|
||||
|
||||
public int TextCount { get; }
|
||||
public int AddedCount { get; }
|
||||
public int RemovedOverrideCount { get; }
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,169 +0,0 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using AibisDream.Framework;
|
||||
using TMPro;
|
||||
using UnityEditor;
|
||||
using UnityEditor.AddressableAssets;
|
||||
using UnityEditor.AddressableAssets.Settings;
|
||||
using UnityEditor.AddressableAssets.Settings.GroupSchemas;
|
||||
using UnityEngine;
|
||||
|
||||
namespace AibisDream.EditorTools
|
||||
{
|
||||
public static class TypographyFirstRoundBuilder
|
||||
{
|
||||
private const string GroupName = "Shared_Typography";
|
||||
private const string ProfileFolder = "Assets/GameContent/Common/Typography/Profiles";
|
||||
private const string WenQuanYiPath = "Assets/Font/Assets/WenQuanYi Bitmap Song 14px SDF.asset";
|
||||
private const string ChillBitmapPath = "Assets/Font/Assets/ChillBitmap_16px SDF.asset";
|
||||
private const string ArkPixelPath = "Assets/Font/TTF/ark-pixel-16px-proportional-latin SDF.asset";
|
||||
|
||||
[MenuItem("Tools/AIBIS Dream/Typography/Rebuild First Round Assets")]
|
||||
public static void BuildFirstRoundAssets()
|
||||
{
|
||||
EnsureFolder(ProfileFolder);
|
||||
|
||||
TMP_FontAsset wenQuanYi = LoadRequiredFont(WenQuanYiPath);
|
||||
TMP_FontAsset chillBitmap = LoadRequiredFont(ChillBitmapPath);
|
||||
TMP_FontAsset arkPixel = LoadRequiredFont(ArkPixelPath);
|
||||
|
||||
AddressableAssetSettings settings = AddressableAssetSettingsDefaultObject.Settings;
|
||||
if (settings == null)
|
||||
throw new InvalidOperationException("AddressableAssetSettings 不存在。");
|
||||
|
||||
AddressableAssetGroup group = GetOrCreateGroup(settings);
|
||||
CreateOrUpdateProfile(
|
||||
settings,
|
||||
group,
|
||||
TypographyProfileKind.ZhHans,
|
||||
wenQuanYi,
|
||||
new[] { chillBitmap });
|
||||
CreateOrUpdateProfile(
|
||||
settings,
|
||||
group,
|
||||
TypographyProfileKind.Japanese,
|
||||
chillBitmap,
|
||||
new[] { wenQuanYi });
|
||||
CreateOrUpdateProfile(
|
||||
settings,
|
||||
group,
|
||||
TypographyProfileKind.Latin,
|
||||
arkPixel,
|
||||
new[] { chillBitmap });
|
||||
CreateOrUpdateProfile(
|
||||
settings,
|
||||
group,
|
||||
TypographyProfileKind.Russian,
|
||||
arkPixel,
|
||||
new[] { chillBitmap });
|
||||
|
||||
TerminalTypographyMigration.MigrateTerminalPrefabs();
|
||||
|
||||
EditorUtility.SetDirty(settings);
|
||||
AssetDatabase.SaveAssets();
|
||||
AssetDatabase.Refresh();
|
||||
Debug.Log("[TypographyFirstRoundBuilder] Profile、Addressables 和首轮试点绑定已更新。");
|
||||
}
|
||||
|
||||
public static void BuildFromCommandLine()
|
||||
{
|
||||
try
|
||||
{
|
||||
BuildFirstRoundAssets();
|
||||
}
|
||||
catch (Exception exception)
|
||||
{
|
||||
Debug.LogException(exception);
|
||||
throw;
|
||||
}
|
||||
}
|
||||
|
||||
private static TMP_FontAsset LoadRequiredFont(string path)
|
||||
{
|
||||
TMP_FontAsset font = AssetDatabase.LoadAssetAtPath<TMP_FontAsset>(path);
|
||||
if (font == null)
|
||||
throw new InvalidOperationException($"找不到字体资产:{path}");
|
||||
return font;
|
||||
}
|
||||
|
||||
private static AddressableAssetGroup GetOrCreateGroup(AddressableAssetSettings settings)
|
||||
{
|
||||
AddressableAssetGroup group = settings.FindGroup(GroupName);
|
||||
if (group == null)
|
||||
{
|
||||
group = settings.CreateGroup(
|
||||
GroupName,
|
||||
false,
|
||||
false,
|
||||
true,
|
||||
null,
|
||||
typeof(BundledAssetGroupSchema),
|
||||
typeof(ContentUpdateGroupSchema));
|
||||
}
|
||||
|
||||
BundledAssetGroupSchema bundled = group.GetSchema<BundledAssetGroupSchema>();
|
||||
if (bundled != null)
|
||||
{
|
||||
bundled.IncludeInBuild = true;
|
||||
bundled.BundleMode = BundledAssetGroupSchema.BundlePackingMode.PackTogether;
|
||||
EditorUtility.SetDirty(bundled);
|
||||
}
|
||||
|
||||
ContentUpdateGroupSchema update = group.GetSchema<ContentUpdateGroupSchema>();
|
||||
if (update != null)
|
||||
{
|
||||
update.StaticContent = false;
|
||||
EditorUtility.SetDirty(update);
|
||||
}
|
||||
|
||||
return group;
|
||||
}
|
||||
|
||||
private static void CreateOrUpdateProfile(
|
||||
AddressableAssetSettings settings,
|
||||
AddressableAssetGroup group,
|
||||
TypographyProfileKind kind,
|
||||
TMP_FontAsset font,
|
||||
IReadOnlyCollection<TMP_FontAsset> fallbacks)
|
||||
{
|
||||
string path = $"{ProfileFolder}/{kind}.asset";
|
||||
TypographyProfile profile = AssetDatabase.LoadAssetAtPath<TypographyProfile>(path);
|
||||
if (profile == null)
|
||||
{
|
||||
profile = ScriptableObject.CreateInstance<TypographyProfile>();
|
||||
AssetDatabase.CreateAsset(profile, path);
|
||||
}
|
||||
|
||||
var styles = new List<TypographyStyle>();
|
||||
foreach (TypographyRole role in Enum.GetValues(typeof(TypographyRole)))
|
||||
{
|
||||
styles.Add(new TypographyStyle(
|
||||
role,
|
||||
font,
|
||||
fallbackFonts: fallbacks,
|
||||
fontSizeMultiplier: 1f));
|
||||
}
|
||||
|
||||
profile.Configure(kind, styles);
|
||||
EditorUtility.SetDirty(profile);
|
||||
|
||||
string guid = AssetDatabase.AssetPathToGUID(path);
|
||||
AddressableAssetEntry entry = settings.CreateOrMoveEntry(guid, group, false, false);
|
||||
entry.address = $"Typography/Profile/{kind}";
|
||||
entry.SetLabel("Typography", true, true, false);
|
||||
}
|
||||
|
||||
private static void EnsureFolder(string path)
|
||||
{
|
||||
string[] segments = path.Split('/');
|
||||
string current = segments[0];
|
||||
for (int i = 1; i < segments.Length; i++)
|
||||
{
|
||||
string next = $"{current}/{segments[i]}";
|
||||
if (!AssetDatabase.IsValidFolder(next))
|
||||
AssetDatabase.CreateFolder(current, segments[i]);
|
||||
current = next;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -22,17 +22,8 @@ public class VersionIncrementer : IPreprocessBuildWithReport
|
||||
int build = int.Parse(versionParts[3]);
|
||||
|
||||
// 更新版本号
|
||||
string incrementedVersion = $"{major}.{minor}.{revision}.{build + 1}";
|
||||
bool isDevelopmentBuild = (report.summary.options & BuildOptions.Development) != 0;
|
||||
|
||||
if (isDevelopmentBuild)
|
||||
{
|
||||
string timestamp = DateTime.Now.ToString("yyyyMMdd-HHmmss");
|
||||
incrementedVersion = $"{incrementedVersion}-{timestamp}-DEV";
|
||||
}
|
||||
|
||||
PlayerSettings.bundleVersion = incrementedVersion;
|
||||
Debug.Log($"[VersionIncrementer] bundleVersion: {incrementedVersion}");
|
||||
string date = DateTime.Now.ToString("yyyyMMdd-HHmmss");
|
||||
PlayerSettings.bundleVersion = $"{major}.{minor}.{revision}.{build + 1}-{date}";
|
||||
|
||||
if (report.summary.platform == BuildTarget.Android)
|
||||
{
|
||||
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user