Compare commits
64
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
3f4fa30b1d | ||
|
|
dd2e09f12e | ||
|
|
9fdb91c8b1 | ||
|
|
5794245a98 | ||
|
|
5e16404d98 | ||
|
|
6f1bead4ab | ||
|
|
89def8621c | ||
|
|
ce860ca734 | ||
|
|
899f803796 | ||
|
|
5cbbbdf03e | ||
|
|
ef47709301 | ||
|
|
7d5e1301a0 | ||
|
|
44ac9ace0a | ||
|
|
2cb37a09a7 | ||
|
|
7867e501b8 | ||
|
|
1ed7b7c881 | ||
|
|
708786e788 | ||
|
|
d0cd0b00b5 | ||
|
|
0e2e61a83b | ||
|
|
5fa324de6c | ||
|
|
93501dddaf | ||
|
|
e6c3148764 | ||
|
|
4739e14d8d | ||
|
|
a088531224 | ||
|
|
2fa3eb8024 | ||
|
|
6c46874e44 | ||
|
|
398bdf18de | ||
|
|
be4e820aa5 | ||
|
|
e8aea1e83d | ||
|
|
fad170c673 | ||
|
|
ba5960b4e7 | ||
|
|
48b1ed4ec9 | ||
|
|
3b1a551a04 | ||
|
|
658aa1889b | ||
|
|
56337c5173 | ||
|
|
3d61fa5944 | ||
|
|
64c7245d9e | ||
|
|
20a458ec78 | ||
|
|
3adb04ebb3 | ||
|
|
3bff294130 | ||
|
|
b0ae7233e0 | ||
|
|
4e2231ef01 | ||
|
|
c77306af0c | ||
|
|
ee08eab9b0 | ||
|
|
24c5169a10 | ||
|
|
48dcc9ec3a | ||
|
|
d675cd1338 | ||
|
|
bd39c27c1d | ||
|
|
01cc5036d3 | ||
|
|
de8d4a5037 | ||
|
|
8bff84640a | ||
|
|
62e05968b7 | ||
|
|
0749df3eb2 | ||
|
|
f113c979a2 | ||
|
|
d9d61be09a | ||
|
|
daa30de7f3 | ||
|
|
6cc19f3d7b | ||
|
|
1df4188994 | ||
|
|
6b46fe3faa | ||
|
|
b211f674d7 | ||
|
|
497ddd45e3 | ||
|
|
7502638281 | ||
|
|
99338a04cc | ||
|
|
38fce43283 |
@@ -67,6 +67,17 @@ 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: ae2a22805ec7ff55174c38995aa3e54d
|
||||
Hash: 2b0c2e120ca52edab004ec49752fe5ec
|
||||
m_OptimizeCatalogSize: 0
|
||||
m_BuildRemoteCatalog: 0
|
||||
m_BundleLocalCatalog: 0
|
||||
@@ -71,6 +71,16 @@ 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
|
||||
@@ -121,6 +131,7 @@ MonoBehaviour:
|
||||
- Locale-pt-BR
|
||||
- Locale-ru
|
||||
- Locale-es
|
||||
- Typography
|
||||
m_SchemaTemplates: []
|
||||
m_GroupTemplateObjects:
|
||||
- {fileID: 11400000, guid: 0c158052680a3e94184c450d00291514, type: 2}
|
||||
|
||||
+31
@@ -0,0 +1,31 @@
|
||||
%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
-1
@@ -1,5 +1,5 @@
|
||||
fileFormatVersion: 2
|
||||
guid: d8dda148dfd9e5745807a2cbecd1f95f
|
||||
guid: 92e4c8e9ee367dd499b7e2f0c1e4fb3a
|
||||
NativeFormatImporter:
|
||||
externalObjects: {}
|
||||
mainObjectFileID: 11400000
|
||||
@@ -0,0 +1,31 @@
|
||||
%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
-1
@@ -1,5 +1,5 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 2f628bf07a7245b4cb27a590db9f51ef
|
||||
guid: 2bf1c0c3a87916a41aa95e5c29efe3d9
|
||||
NativeFormatImporter:
|
||||
externalObjects: {}
|
||||
mainObjectFileID: 11400000
|
||||
@@ -0,0 +1,31 @@
|
||||
%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}
|
||||
+1
-1
@@ -1,5 +1,5 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 1b9419cf5ebdeb449a112d42f4205c33
|
||||
guid: f91efa4c655c0014ca776fb2e8b4f1fc
|
||||
NativeFormatImporter:
|
||||
externalObjects: {}
|
||||
mainObjectFileID: 11400000
|
||||
+1
-7
@@ -16,13 +16,7 @@ MonoBehaviour:
|
||||
m_Data:
|
||||
m_SerializedData: []
|
||||
m_GUID: b848f030a719ba04c9f1e343f17ddc0c
|
||||
m_SerializeEntries:
|
||||
- m_GUID: 6d8ee72f781acf64abfe82ab659f5771
|
||||
m_Address: 03_qq_symbol
|
||||
m_ReadOnly: 1
|
||||
m_SerializedLabels:
|
||||
- Locale-zh-Hans
|
||||
FlaggedDuringContentUpdateRestriction: 0
|
||||
m_SerializeEntries: []
|
||||
m_ReadOnly: 1
|
||||
m_Settings: {fileID: 11400000, guid: 77169ce22e430f64fb36c771815a4a7b, type: 2}
|
||||
m_SchemaSet:
|
||||
|
||||
+25
@@ -0,0 +1,25 @@
|
||||
%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}
|
||||
+1
-1
@@ -1,5 +1,5 @@
|
||||
fileFormatVersion: 2
|
||||
guid: c0f4601f141d314479c22f4bb3efd5a7
|
||||
guid: 2b05cac5050c285498c6d11b749f865a
|
||||
NativeFormatImporter:
|
||||
externalObjects: {}
|
||||
mainObjectFileID: 11400000
|
||||
@@ -40,18 +40,33 @@ MonoBehaviour:
|
||||
m_ReadOnly: 1
|
||||
m_SerializedLabels: []
|
||||
FlaggedDuringContentUpdateRestriction: 0
|
||||
- m_GUID: 032b1b5e09139ae45b6de51d35baae79
|
||||
m_Address: discord-square-color-icon
|
||||
- m_GUID: 539db7533ab287b45a774b3c5e2ccd9d
|
||||
m_Address: "\u7ED3\u5C3E\u56FE\u6807"
|
||||
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
@@ -0,0 +1,45 @@
|
||||
%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
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 29e8470e79377b4409a648ee18ce7682
|
||||
NativeFormatImporter:
|
||||
externalObjects: {}
|
||||
mainObjectFileID: 11400000
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
+16
@@ -0,0 +1,16 @@
|
||||
%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
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 04b28f4d862df2440ac0be17bd1cd694
|
||||
NativeFormatImporter:
|
||||
externalObjects: {}
|
||||
mainObjectFileID: 11400000
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
+45
@@ -0,0 +1,45 @@
|
||||
%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
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: ffaea4b400f30fd43bb702a14172406c
|
||||
NativeFormatImporter:
|
||||
externalObjects: {}
|
||||
mainObjectFileID: 11400000
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
+16
@@ -0,0 +1,16 @@
|
||||
%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
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 42d59bddf89a08140adb832b309cf5a7
|
||||
NativeFormatImporter:
|
||||
externalObjects: {}
|
||||
mainObjectFileID: 11400000
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
+45
@@ -0,0 +1,45 @@
|
||||
%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
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 779a71a2538b963459fd1ef9cc055b7c
|
||||
NativeFormatImporter:
|
||||
externalObjects: {}
|
||||
mainObjectFileID: 11400000
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
+16
@@ -0,0 +1,16 @@
|
||||
%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
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 90fabd6fb037ad0489aaccead4298af4
|
||||
NativeFormatImporter:
|
||||
externalObjects: {}
|
||||
mainObjectFileID: 11400000
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
+45
@@ -0,0 +1,45 @@
|
||||
%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
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: d3a6468425a79364fa21d96e369e8399
|
||||
NativeFormatImporter:
|
||||
externalObjects: {}
|
||||
mainObjectFileID: 11400000
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
+16
@@ -0,0 +1,16 @@
|
||||
%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
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 2a43b55ded538f14f92548425cfb42e5
|
||||
NativeFormatImporter:
|
||||
externalObjects: {}
|
||||
mainObjectFileID: 11400000
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
+45
@@ -0,0 +1,45 @@
|
||||
%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
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: d477ca02df8260c4ba977ebfe3a551fb
|
||||
NativeFormatImporter:
|
||||
externalObjects: {}
|
||||
mainObjectFileID: 11400000
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
+16
@@ -0,0 +1,16 @@
|
||||
%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
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 7f3b2267eaa2569429428b05d408314e
|
||||
NativeFormatImporter:
|
||||
externalObjects: {}
|
||||
mainObjectFileID: 11400000
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
+45
@@ -0,0 +1,45 @@
|
||||
%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
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 196a7a55916e25f4896599ddb2660f7a
|
||||
NativeFormatImporter:
|
||||
externalObjects: {}
|
||||
mainObjectFileID: 11400000
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
+16
@@ -0,0 +1,16 @@
|
||||
%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
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: a40977b297f51304cb63f1a2787ea4a4
|
||||
NativeFormatImporter:
|
||||
externalObjects: {}
|
||||
mainObjectFileID: 11400000
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
+45
@@ -0,0 +1,45 @@
|
||||
%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
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: a2d74ab6ace13ab4ba87121fa11aea2c
|
||||
NativeFormatImporter:
|
||||
externalObjects: {}
|
||||
mainObjectFileID: 11400000
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
+16
@@ -0,0 +1,16 @@
|
||||
%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
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: b4ec695ab7e7a7848a6e4df4156f5091
|
||||
NativeFormatImporter:
|
||||
externalObjects: {}
|
||||
mainObjectFileID: 11400000
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
+45
@@ -0,0 +1,45 @@
|
||||
%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
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 7219f667c0a45a642a5b943eb602dffc
|
||||
NativeFormatImporter:
|
||||
externalObjects: {}
|
||||
mainObjectFileID: 11400000
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
+16
@@ -0,0 +1,16 @@
|
||||
%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
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 6f490fe007fca8342aceea06f8b0e054
|
||||
NativeFormatImporter:
|
||||
externalObjects: {}
|
||||
mainObjectFileID: 11400000
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
+45
@@ -0,0 +1,45 @@
|
||||
%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
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 955397ef95649794a87b3c52b818f5c1
|
||||
NativeFormatImporter:
|
||||
externalObjects: {}
|
||||
mainObjectFileID: 11400000
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
+16
@@ -0,0 +1,16 @@
|
||||
%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
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 5012207ff0e21364d9462964eb6374ef
|
||||
NativeFormatImporter:
|
||||
externalObjects: {}
|
||||
mainObjectFileID: 11400000
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
+45
@@ -0,0 +1,45 @@
|
||||
%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
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: b39cbcc4de75ad6468842c36c7e5138d
|
||||
NativeFormatImporter:
|
||||
externalObjects: {}
|
||||
mainObjectFileID: 11400000
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
+16
@@ -0,0 +1,16 @@
|
||||
%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
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 6bd7e9bcac1e24042b79a54f47f52106
|
||||
NativeFormatImporter:
|
||||
externalObjects: {}
|
||||
mainObjectFileID: 11400000
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,30 @@
|
||||
%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}
|
||||
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: b05cfdc6bbe312248b8be84a1ff01e32
|
||||
NativeFormatImporter:
|
||||
externalObjects: {}
|
||||
mainObjectFileID: 11400000
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,30 @@
|
||||
%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}
|
||||
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 0418ce3bee30b98409aa4d268d7d89d7
|
||||
NativeFormatImporter:
|
||||
externalObjects: {}
|
||||
mainObjectFileID: 11400000
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,30 @@
|
||||
%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}
|
||||
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 6e0c8dfd5e5e1c74b80cecc21df2d9cd
|
||||
NativeFormatImporter:
|
||||
externalObjects: {}
|
||||
mainObjectFileID: 11400000
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,30 @@
|
||||
%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}
|
||||
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: f0205c268777acb459dddbb4690d7c27
|
||||
NativeFormatImporter:
|
||||
externalObjects: {}
|
||||
mainObjectFileID: 11400000
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,30 @@
|
||||
%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}
|
||||
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: d029169cdb68ba848a0f0260aeddae55
|
||||
NativeFormatImporter:
|
||||
externalObjects: {}
|
||||
mainObjectFileID: 11400000
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,49 @@
|
||||
%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}
|
||||
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 1051221ba75fcea47b67648acedc5383
|
||||
NativeFormatImporter:
|
||||
externalObjects: {}
|
||||
mainObjectFileID: 11400000
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -1,260 +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 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,123 +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 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,117 +0,0 @@
|
||||
#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,476 +0,0 @@
|
||||
#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,77 +0,0 @@
|
||||
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,577 +0,0 @@
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,11 +0,0 @@
|
||||
fileFormatVersion: 2
|
||||
guid: d6b8797332ac4876b66e381ea3c98723
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -1,216 +0,0 @@
|
||||
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));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,11 +0,0 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 5f75b14634314395a3a67721065f86bd
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -1,5 +1,5 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 638cd3fa1c2470d42aceb69214c01786
|
||||
guid: d3a91a90ebc2fd742bd10a66893940b8
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
@@ -1,44 +0,0 @@
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,11 +0,0 @@
|
||||
fileFormatVersion: 2
|
||||
guid: d5e819fca9154e979c37ac490184620b
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: d6e75cb49ad84a8ebe0520a529e7a67e
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,50 @@
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 23aa0707ef5c4618b55c455f2a03a612
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,152 @@
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,131 @@
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 7f556352cf7f71c4a98e967b54dffc3b
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
+29
-55
@@ -5,10 +5,10 @@ using NUnit.Framework;
|
||||
|
||||
namespace AibisDream.SystemEditor.Tests
|
||||
{
|
||||
public sealed class PresentationSnapshotContractTests
|
||||
public sealed class SaveSnapshotContractTests
|
||||
{
|
||||
[Test]
|
||||
public void PresentationProviders_UseStableIdsAndRestoreInDependencyOrder()
|
||||
public void PresentationProviders_KeepStableIdsAndRestoreOrder()
|
||||
{
|
||||
var showcase = new ShowcaseSnapshotProvider();
|
||||
var day2Sleep = new Day2SleepPresentationSnapshotProvider();
|
||||
@@ -24,31 +24,7 @@ namespace AibisDream.SystemEditor.Tests
|
||||
}
|
||||
|
||||
[Test]
|
||||
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()
|
||||
public void PresentationSections_RoundTripAndLegacySnapshotsRemainReadable()
|
||||
{
|
||||
var source = new SaveSnapshot();
|
||||
source.sections[SnapshotProviderIds.Showcase] = new ShowcaseSnapshotDto
|
||||
@@ -57,42 +33,40 @@ namespace AibisDream.SystemEditor.Tests
|
||||
picName = "D2S星图",
|
||||
isBackgroundVisible = true
|
||||
};
|
||||
source.sections[SnapshotProviderIds.Day2SleepPresentation] =
|
||||
new Day2SleepPresentationSnapshotDto
|
||||
{
|
||||
mode = Day2SleepPresentationMode.LargeEffects.ToString(),
|
||||
largeBlurAmount = 0.72f,
|
||||
largeBlurSize = 0.015f,
|
||||
largeScaleMultiplier = 1.1f,
|
||||
largeAlpha = 0.4f
|
||||
};
|
||||
source.sections[SnapshotProviderIds.PlayTool] = new PlayToolSnapshotDto
|
||||
{
|
||||
isObjVisible = true,
|
||||
objPicName = "证物",
|
||||
objIsLocalized = true,
|
||||
isFullScreenVisible = true,
|
||||
fullScreenPicName = "病历",
|
||||
fullScreenIsLocalized = true
|
||||
};
|
||||
|
||||
var json = JsonConvert.SerializeObject(source);
|
||||
var restored = JsonConvert.DeserializeObject<SaveSnapshot>(json);
|
||||
var restored = JsonConvert.DeserializeObject<SaveSnapshot>(
|
||||
JsonConvert.SerializeObject(source));
|
||||
var showcase = ((JObject)restored.sections[SnapshotProviderIds.Showcase])
|
||||
.ToObject<ShowcaseSnapshotDto>();
|
||||
var day2Sleep = ((JObject)restored.sections[SnapshotProviderIds.Day2SleepPresentation])
|
||||
.ToObject<Day2SleepPresentationSnapshotDto>();
|
||||
var playTool = ((JObject)restored.sections[SnapshotProviderIds.PlayTool])
|
||||
.ToObject<PlayToolSnapshotDto>();
|
||||
|
||||
Assert.That(restored.schemaVersion, Is.EqualTo(SaveSnapshotSchema.CurrentVersion));
|
||||
Assert.That(showcase.displayMode, Is.EqualTo("Large"));
|
||||
Assert.That(showcase.picName, Is.EqualTo("D2S星图"));
|
||||
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));
|
||||
}
|
||||
Assert.That(playTool.objPicName, Is.EqualTo("证物"));
|
||||
Assert.That(playTool.objIsLocalized, Is.True);
|
||||
Assert.That(playTool.fullScreenPicName, Is.EqualTo("病历"));
|
||||
Assert.That(playTool.fullScreenIsLocalized, Is.True);
|
||||
|
||||
[Test]
|
||||
public void OldSnapshotWithoutPresentationSections_DeserializesWithoutSynthesizingSections()
|
||||
{
|
||||
var restored = JsonConvert.DeserializeObject<SaveSnapshot>(
|
||||
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>(
|
||||
"{\"schemaVersion\":1,\"sections\":{}}");
|
||||
|
||||
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);
|
||||
Assert.That(legacy.schemaVersion, Is.EqualTo(1));
|
||||
Assert.That(legacy.sections, Is.Empty);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,94 @@
|
||||
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));
|
||||
}
|
||||
}
|
||||
}
|
||||
+3
-3
@@ -6,6 +6,6 @@ MonoImporter:
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,182 @@
|
||||
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
-1
@@ -1,5 +1,5 @@
|
||||
fileFormatVersion: 2
|
||||
guid: cbbba0c3621cf204483f453bad2d75a4
|
||||
guid: 895c6fa68b9b4ed4286362a258247f4a
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
@@ -0,0 +1,75 @@
|
||||
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>())
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: a9b456d04f99d9849a0200e688ddfb39
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -1,75 +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();
|
||||
}
|
||||
|
||||
[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,5 +1,5 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 665561f3805a96446b45ea1d50bfe520
|
||||
guid: 1ccca9c8cbbc67e47be376cc9db7ce55
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
@@ -0,0 +1,229 @@
|
||||
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
-1
@@ -1,5 +1,5 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 23aa0707ef5c4618b55c455f2a03a612
|
||||
guid: 72a6797e130a7f643bd42bf648543ec2
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
@@ -0,0 +1,169 @@
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
+1
-1
@@ -1,5 +1,5 @@
|
||||
fileFormatVersion: 2
|
||||
guid: b95ba9d1e80e3dc4e83bc408569c4955
|
||||
guid: a84a91ac052fc3444aa281c7d6a4f29e
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
@@ -22,8 +22,17 @@ public class VersionIncrementer : IPreprocessBuildWithReport
|
||||
int build = int.Parse(versionParts[3]);
|
||||
|
||||
// 更新版本号
|
||||
string date = DateTime.Now.ToString("yyyyMMdd-HHmmss");
|
||||
PlayerSettings.bundleVersion = $"{major}.{minor}.{revision}.{build + 1}-{date}";
|
||||
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}");
|
||||
|
||||
if (report.summary.platform == BuildTarget.Android)
|
||||
{
|
||||
@@ -32,4 +41,4 @@ public class VersionIncrementer : IPreprocessBuildWithReport
|
||||
Debug.Log($"[VersionIncrementer] Android versionCode: {versionCode} -> {versionCode + 1}");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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