feat: 受击音效更新类别控制
This commit is contained in:
@@ -6,7 +6,10 @@
|
|||||||
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003AAimTargetFinder_002Ecs_002Fl_003AC_0021_003FUsers_003FLenovo_003FAppData_003FRoaming_003FJetBrains_003FRider2025_002E1_003Fresharper_002Dhost_003FDecompilerCache_003Fdecompiler_003F492b6cef9eac4cd497c1ffcbfcb9161f197200_003Fa9_003F4ac92f25_003FAimTargetFinder_002Ecs/@EntryIndexedValue">ForceIncluded</s:String>
|
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003AAimTargetFinder_002Ecs_002Fl_003AC_0021_003FUsers_003FLenovo_003FAppData_003FRoaming_003FJetBrains_003FRider2025_002E1_003Fresharper_002Dhost_003FDecompilerCache_003Fdecompiler_003F492b6cef9eac4cd497c1ffcbfcb9161f197200_003Fa9_003F4ac92f25_003FAimTargetFinder_002Ecs/@EntryIndexedValue">ForceIncluded</s:String>
|
||||||
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003AAudioManager_002Ecs_002Fl_003AC_0021_003FUsers_003FLenovo_003FAppData_003FRoaming_003FJetBrains_003FRider2025_002E1_003Fresharper_002Dhost_003FDecompilerCache_003Fdecompiler_003Fae7b55f2e79e4a30b19151f53aa9af29197600_003F95_003F5092a809_003FAudioManager_002Ecs/@EntryIndexedValue">ForceIncluded</s:String>
|
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003AAudioManager_002Ecs_002Fl_003AC_0021_003FUsers_003FLenovo_003FAppData_003FRoaming_003FJetBrains_003FRider2025_002E1_003Fresharper_002Dhost_003FDecompilerCache_003Fdecompiler_003Fae7b55f2e79e4a30b19151f53aa9af29197600_003F95_003F5092a809_003FAudioManager_002Ecs/@EntryIndexedValue">ForceIncluded</s:String>
|
||||||
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003ABehaviour_002Ecs_002Fl_003AC_0021_003FUsers_003FLenovo_003FAppData_003FRoaming_003FJetBrains_003FRider2025_002E1_003Fresharper_002Dhost_003FDecompilerCache_003Fdecompiler_003Fc39a522eee05469b8171a6cfeb646c591543b0_003Fc1_003Faa1a31b2_003FBehaviour_002Ecs/@EntryIndexedValue">ForceIncluded</s:String>
|
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003ABehaviour_002Ecs_002Fl_003AC_0021_003FUsers_003FLenovo_003FAppData_003FRoaming_003FJetBrains_003FRider2025_002E1_003Fresharper_002Dhost_003FDecompilerCache_003Fdecompiler_003Fc39a522eee05469b8171a6cfeb646c591543b0_003Fc1_003Faa1a31b2_003FBehaviour_002Ecs/@EntryIndexedValue">ForceIncluded</s:String>
|
||||||
|
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003ABlackScreen_002Ecs_002Fl_003AC_0021_003FUsers_003FLenovo_003FAppData_003FRoaming_003FJetBrains_003FRider2025_002E1_003Fresharper_002Dhost_003FDecompilerCache_003Fdecompiler_003Fae7b55f2e79e4a30b19151f53aa9af29197600_003F83_003Fdd84d8e1_003FBlackScreen_002Ecs/@EntryIndexedValue">ForceIncluded</s:String>
|
||||||
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003ABuffsDisplay_002Ecs_002Fl_003AC_0021_003FUsers_003FLenovo_003FAppData_003FRoaming_003FJetBrains_003FRider2025_002E1_003Fresharper_002Dhost_003FDecompilerCache_003Fdecompiler_003Fae7b55f2e79e4a30b19151f53aa9af29197600_003F72_003F6940c184_003FBuffsDisplay_002Ecs/@EntryIndexedValue">ForceIncluded</s:String>
|
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003ABuffsDisplay_002Ecs_002Fl_003AC_0021_003FUsers_003FLenovo_003FAppData_003FRoaming_003FJetBrains_003FRider2025_002E1_003Fresharper_002Dhost_003FDecompilerCache_003Fdecompiler_003Fae7b55f2e79e4a30b19151f53aa9af29197600_003F72_003F6940c184_003FBuffsDisplay_002Ecs/@EntryIndexedValue">ForceIncluded</s:String>
|
||||||
|
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003ABuff_002Ecs_002Fl_003AC_0021_003FUsers_003FLenovo_003FAppData_003FRoaming_003FJetBrains_003FRider2025_002E1_003Fresharper_002Dhost_003FDecompilerCache_003Fdecompiler_003Fae7b55f2e79e4a30b19151f53aa9af29197600_003Fc9_003F95f738f9_003FBuff_002Ecs/@EntryIndexedValue">ForceIncluded</s:String>
|
||||||
|
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003AButton_005FLoadMainMenu_002Ecs_002Fl_003AC_0021_003FUsers_003FLenovo_003FAppData_003FRoaming_003FJetBrains_003FRider2025_002E1_003Fresharper_002Dhost_003FDecompilerCache_003Fdecompiler_003Fae7b55f2e79e4a30b19151f53aa9af29197600_003Fae_003F1c447fe1_003FButton_005FLoadMainMenu_002Ecs/@EntryIndexedValue">ForceIncluded</s:String>
|
||||||
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003ACameraArmControl_002Ecs_002Fl_003AC_0021_003FUsers_003FLenovo_003FAppData_003FRoaming_003FJetBrains_003FRider2025_002E1_003Fresharper_002Dhost_003FDecompilerCache_003Fdecompiler_003F492b6cef9eac4cd497c1ffcbfcb9161f197200_003Ff5_003F4622470d_003FCameraArmControl_002Ecs/@EntryIndexedValue">ForceIncluded</s:String>
|
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003ACameraArmControl_002Ecs_002Fl_003AC_0021_003FUsers_003FLenovo_003FAppData_003FRoaming_003FJetBrains_003FRider2025_002E1_003Fresharper_002Dhost_003FDecompilerCache_003Fdecompiler_003F492b6cef9eac4cd497c1ffcbfcb9161f197200_003Ff5_003F4622470d_003FCameraArmControl_002Ecs/@EntryIndexedValue">ForceIncluded</s:String>
|
||||||
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003ACameraArm_002Ecs_002Fl_003AC_0021_003FUsers_003FLenovo_003FAppData_003FRoaming_003FJetBrains_003FRider2025_002E1_003Fresharper_002Dhost_003FDecompilerCache_003Fdecompiler_003F492b6cef9eac4cd497c1ffcbfcb9161f197200_003Fdc_003Fc73e8a9f_003FCameraArm_002Ecs/@EntryIndexedValue">ForceIncluded</s:String>
|
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003ACameraArm_002Ecs_002Fl_003AC_0021_003FUsers_003FLenovo_003FAppData_003FRoaming_003FJetBrains_003FRider2025_002E1_003Fresharper_002Dhost_003FDecompilerCache_003Fdecompiler_003F492b6cef9eac4cd497c1ffcbfcb9161f197200_003Fdc_003Fc73e8a9f_003FCameraArm_002Ecs/@EntryIndexedValue">ForceIncluded</s:String>
|
||||||
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003ACameraArm_002Ecs_002Fl_003AC_0021_003FUsers_003FLenovo_003FAppData_003FRoaming_003FJetBrains_003FRider2025_002E1_003Fresharper_002Dhost_003FDecompilerCache_003Fdecompiler_003Fae7b55f2e79e4a30b19151f53aa9af29197600_003Ff6_003F5678008c_003FCameraArm_002Ecs/@EntryIndexedValue">ForceIncluded</s:String>
|
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003ACameraArm_002Ecs_002Fl_003AC_0021_003FUsers_003FLenovo_003FAppData_003FRoaming_003FJetBrains_003FRider2025_002E1_003Fresharper_002Dhost_003FDecompilerCache_003Fdecompiler_003Fae7b55f2e79e4a30b19151f53aa9af29197600_003Ff6_003F5678008c_003FCameraArm_002Ecs/@EntryIndexedValue">ForceIncluded</s:String>
|
||||||
@@ -17,14 +20,23 @@
|
|||||||
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003ACharacterMainControl_002Ecs_002Fl_003AC_0021_003FUsers_003FLenovo_003FAppData_003FRoaming_003FJetBrains_003FRider2025_002E1_003Fresharper_002Dhost_003FDecompilerCache_003Fdecompiler_003Fae7b55f2e79e4a30b19151f53aa9af29197600_003F4c_003Faffc257a_003FCharacterMainControl_002Ecs/@EntryIndexedValue">ForceIncluded</s:String>
|
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003ACharacterMainControl_002Ecs_002Fl_003AC_0021_003FUsers_003FLenovo_003FAppData_003FRoaming_003FJetBrains_003FRider2025_002E1_003Fresharper_002Dhost_003FDecompilerCache_003Fdecompiler_003Fae7b55f2e79e4a30b19151f53aa9af29197600_003F4c_003Faffc257a_003FCharacterMainControl_002Ecs/@EntryIndexedValue">ForceIncluded</s:String>
|
||||||
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003ACopyTextButton_002Ecs_002Fl_003AC_0021_003FUsers_003FLenovo_003FAppData_003FRoaming_003FJetBrains_003FRider2025_002E1_003Fresharper_002Dhost_003FDecompilerCache_003Fdecompiler_003Fae7b55f2e79e4a30b19151f53aa9af29197600_003Fc4_003F9995fb11_003FCopyTextButton_002Ecs/@EntryIndexedValue">ForceIncluded</s:String>
|
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003ACopyTextButton_002Ecs_002Fl_003AC_0021_003FUsers_003FLenovo_003FAppData_003FRoaming_003FJetBrains_003FRider2025_002E1_003Fresharper_002Dhost_003FDecompilerCache_003Fdecompiler_003Fae7b55f2e79e4a30b19151f53aa9af29197600_003Fc4_003F9995fb11_003FCopyTextButton_002Ecs/@EntryIndexedValue">ForceIncluded</s:String>
|
||||||
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003ACustomFaceInstance_002Ecs_002Fl_003AC_0021_003FUsers_003FLenovo_003FAppData_003FRoaming_003FJetBrains_003FRider2025_002E1_003Fresharper_002Dhost_003FDecompilerCache_003Fdecompiler_003Fae7b55f2e79e4a30b19151f53aa9af29197600_003Ff5_003F7e299618_003FCustomFaceInstance_002Ecs/@EntryIndexedValue">ForceIncluded</s:String>
|
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003ACustomFaceInstance_002Ecs_002Fl_003AC_0021_003FUsers_003FLenovo_003FAppData_003FRoaming_003FJetBrains_003FRider2025_002E1_003Fresharper_002Dhost_003FDecompilerCache_003Fdecompiler_003Fae7b55f2e79e4a30b19151f53aa9af29197600_003Ff5_003F7e299618_003FCustomFaceInstance_002Ecs/@EntryIndexedValue">ForceIncluded</s:String>
|
||||||
|
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003ADamageInfo_002Ecs_002Fl_003AC_0021_003FUsers_003FLenovo_003FAppData_003FRoaming_003FJetBrains_003FRider2025_002E1_003Fresharper_002Dhost_003FDecompilerCache_003Fdecompiler_003Fae7b55f2e79e4a30b19151f53aa9af29197600_003F0b_003F36368d2b_003FDamageInfo_002Ecs/@EntryIndexedValue">ForceIncluded</s:String>
|
||||||
|
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003ADamageTypes_002Ecs_002Fl_003AC_0021_003FUsers_003FLenovo_003FAppData_003FRoaming_003FJetBrains_003FRider2025_002E1_003Fresharper_002Dhost_003FDecompilerCache_003Fdecompiler_003Fae7b55f2e79e4a30b19151f53aa9af29197600_003Faf_003Fe7c272fb_003FDamageTypes_002Ecs/@EntryIndexedValue">ForceIncluded</s:String>
|
||||||
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003ADuckovScreenCapturer_002Ecs_002Fl_003AC_0021_003FUsers_003FLenovo_003FAppData_003FRoaming_003FJetBrains_003FRider2025_002E1_003Fresharper_002Dhost_003FDecompilerCache_003Fdecompiler_003F492b6cef9eac4cd497c1ffcbfcb9161f197200_003F59_003F92754824_003FDuckovScreenCapturer_002Ecs/@EntryIndexedValue">ForceIncluded</s:String>
|
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003ADuckovScreenCapturer_002Ecs_002Fl_003AC_0021_003FUsers_003FLenovo_003FAppData_003FRoaming_003FJetBrains_003FRider2025_002E1_003Fresharper_002Dhost_003FDecompilerCache_003Fdecompiler_003F492b6cef9eac4cd497c1ffcbfcb9161f197200_003F59_003F92754824_003FDuckovScreenCapturer_002Ecs/@EntryIndexedValue">ForceIncluded</s:String>
|
||||||
|
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003AFadeElement_002Ecs_002Fl_003AC_0021_003FUsers_003FLenovo_003FAppData_003FRoaming_003FJetBrains_003FRider2025_002E1_003Fresharper_002Dhost_003FDecompilerCache_003Fdecompiler_003Fae7b55f2e79e4a30b19151f53aa9af29197600_003F9b_003Fbe78cf8b_003FFadeElement_002Ecs/@EntryIndexedValue">ForceIncluded</s:String>
|
||||||
|
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003AFadeGroup_002Ecs_002Fl_003AC_0021_003FUsers_003FLenovo_003FAppData_003FRoaming_003FJetBrains_003FRider2025_002E1_003Fresharper_002Dhost_003FDecompilerCache_003Fdecompiler_003Fae7b55f2e79e4a30b19151f53aa9af29197600_003F4e_003F69f8fcae_003FFadeGroup_002Ecs/@EntryIndexedValue">ForceIncluded</s:String>
|
||||||
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003AGameCamera_002Ecs_002Fl_003AC_0021_003FUsers_003FLenovo_003FAppData_003FRoaming_003FJetBrains_003FRider2025_002E1_003Fresharper_002Dhost_003FDecompilerCache_003Fdecompiler_003F492b6cef9eac4cd497c1ffcbfcb9161f197200_003Fd2_003F4160411a_003FGameCamera_002Ecs/@EntryIndexedValue">ForceIncluded</s:String>
|
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003AGameCamera_002Ecs_002Fl_003AC_0021_003FUsers_003FLenovo_003FAppData_003FRoaming_003FJetBrains_003FRider2025_002E1_003Fresharper_002Dhost_003FDecompilerCache_003Fdecompiler_003F492b6cef9eac4cd497c1ffcbfcb9161f197200_003Fd2_003F4160411a_003FGameCamera_002Ecs/@EntryIndexedValue">ForceIncluded</s:String>
|
||||||
|
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003AGameManager_002Ecs_002Fl_003AC_0021_003FUsers_003FLenovo_003FAppData_003FRoaming_003FJetBrains_003FRider2025_002E1_003Fresharper_002Dhost_003FDecompilerCache_003Fdecompiler_003Fae7b55f2e79e4a30b19151f53aa9af29197600_003F94_003Fdd33b16b_003FGameManager_002Ecs/@EntryIndexedValue">ForceIncluded</s:String>
|
||||||
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003AGameplayDataSettings_002Ecs_002Fl_003AC_0021_003FUsers_003FLenovo_003FAppData_003FRoaming_003FJetBrains_003FRider2025_002E1_003Fresharper_002Dhost_003FDecompilerCache_003Fdecompiler_003F492b6cef9eac4cd497c1ffcbfcb9161f197200_003Fa4_003Ffea6779d_003FGameplayDataSettings_002Ecs/@EntryIndexedValue">ForceIncluded</s:String>
|
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003AGameplayDataSettings_002Ecs_002Fl_003AC_0021_003FUsers_003FLenovo_003FAppData_003FRoaming_003FJetBrains_003FRider2025_002E1_003Fresharper_002Dhost_003FDecompilerCache_003Fdecompiler_003F492b6cef9eac4cd497c1ffcbfcb9161f197200_003Fa4_003Ffea6779d_003FGameplayDataSettings_002Ecs/@EntryIndexedValue">ForceIncluded</s:String>
|
||||||
|
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003AGameplayDataSettings_002Ecs_002Fl_003AC_0021_003FUsers_003FLenovo_003FAppData_003FRoaming_003FJetBrains_003FRider2025_002E1_003Fresharper_002Dhost_003FDecompilerCache_003Fdecompiler_003Fae7b55f2e79e4a30b19151f53aa9af29197600_003F1f_003F8b814bde_003FGameplayDataSettings_002Ecs/@EntryIndexedValue">ForceIncluded</s:String>
|
||||||
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003AGameplayUIManager_002Ecs_002Fl_003AC_0021_003FUsers_003FLenovo_003FAppData_003FRoaming_003FJetBrains_003FRider2025_002E1_003Fresharper_002Dhost_003FDecompilerCache_003Fdecompiler_003Fae7b55f2e79e4a30b19151f53aa9af29197600_003Fcc_003Fe767eb5a_003FGameplayUIManager_002Ecs/@EntryIndexedValue">ForceIncluded</s:String>
|
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003AGameplayUIManager_002Ecs_002Fl_003AC_0021_003FUsers_003FLenovo_003FAppData_003FRoaming_003FJetBrains_003FRider2025_002E1_003Fresharper_002Dhost_003FDecompilerCache_003Fdecompiler_003Fae7b55f2e79e4a30b19151f53aa9af29197600_003Fcc_003Fe767eb5a_003FGameplayUIManager_002Ecs/@EntryIndexedValue">ForceIncluded</s:String>
|
||||||
|
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003AGameRulesManager_002Ecs_002Fl_003AC_0021_003FUsers_003FLenovo_003FAppData_003FRoaming_003FJetBrains_003FRider2025_002E1_003Fresharper_002Dhost_003FDecompilerCache_003Fdecompiler_003Fae7b55f2e79e4a30b19151f53aa9af29197600_003Fb1_003Ff82c3168_003FGameRulesManager_002Ecs/@EntryIndexedValue">ForceIncluded</s:String>
|
||||||
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003AInventoryData_002Ecs_002Fl_003AC_0021_003FUsers_003FLenovo_003FAppData_003FRoaming_003FJetBrains_003FRider2025_002E1_003Fresharper_002Dhost_003FDecompilerCache_003Fdecompiler_003F71a3d79537274de2b084d0fd207549d617200_003F5e_003F6152b6b7_003FInventoryData_002Ecs/@EntryIndexedValue">ForceIncluded</s:String>
|
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003AInventoryData_002Ecs_002Fl_003AC_0021_003FUsers_003FLenovo_003FAppData_003FRoaming_003FJetBrains_003FRider2025_002E1_003Fresharper_002Dhost_003FDecompilerCache_003Fdecompiler_003F71a3d79537274de2b084d0fd207549d617200_003F5e_003F6152b6b7_003FInventoryData_002Ecs/@EntryIndexedValue">ForceIncluded</s:String>
|
||||||
|
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003AItemDisplay_002Ecs_002Fl_003AC_0021_003FUsers_003FLenovo_003FAppData_003FRoaming_003FJetBrains_003FRider2025_002E1_003Fresharper_002Dhost_003FDecompilerCache_003Fdecompiler_003Fae7b55f2e79e4a30b19151f53aa9af29197600_003F73_003Fa6db3a6c_003FItemDisplay_002Ecs/@EntryIndexedValue">ForceIncluded</s:String>
|
||||||
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003ALevelConfig_002Ecs_002Fl_003AC_0021_003FUsers_003FLenovo_003FAppData_003FRoaming_003FJetBrains_003FRider2025_002E1_003Fresharper_002Dhost_003FDecompilerCache_003Fdecompiler_003F492b6cef9eac4cd497c1ffcbfcb9161f197200_003Fcc_003Fcfd90334_003FLevelConfig_002Ecs/@EntryIndexedValue">ForceIncluded</s:String>
|
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003ALevelConfig_002Ecs_002Fl_003AC_0021_003FUsers_003FLenovo_003FAppData_003FRoaming_003FJetBrains_003FRider2025_002E1_003Fresharper_002Dhost_003FDecompilerCache_003Fdecompiler_003F492b6cef9eac4cd497c1ffcbfcb9161f197200_003Fcc_003Fcfd90334_003FLevelConfig_002Ecs/@EntryIndexedValue">ForceIncluded</s:String>
|
||||||
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003ALevelManager_002Ecs_002Fl_003AC_0021_003FUsers_003FLenovo_003FAppData_003FRoaming_003FJetBrains_003FRider2025_002E1_003Fresharper_002Dhost_003FDecompilerCache_003Fdecompiler_003Fae7b55f2e79e4a30b19151f53aa9af29197600_003Fe0_003F6f24caa1_003FLevelManager_002Ecs/@EntryIndexedValue">ForceIncluded</s:String>
|
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003ALevelManager_002Ecs_002Fl_003AC_0021_003FUsers_003FLenovo_003FAppData_003FRoaming_003FJetBrains_003FRider2025_002E1_003Fresharper_002Dhost_003FDecompilerCache_003Fdecompiler_003Fae7b55f2e79e4a30b19151f53aa9af29197600_003Fe0_003F6f24caa1_003FLevelManager_002Ecs/@EntryIndexedValue">ForceIncluded</s:String>
|
||||||
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003AMainCharacterStatValueDisplay_002Ecs_002Fl_003AC_0021_003FUsers_003FLenovo_003FAppData_003FRoaming_003FJetBrains_003FRider2025_002E1_003Fresharper_002Dhost_003FDecompilerCache_003Fdecompiler_003Fae7b55f2e79e4a30b19151f53aa9af29197600_003F08_003Fd04928fd_003FMainCharacterStatValueDisplay_002Ecs/@EntryIndexedValue">ForceIncluded</s:String>
|
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003AMainCharacterStatValueDisplay_002Ecs_002Fl_003AC_0021_003FUsers_003FLenovo_003FAppData_003FRoaming_003FJetBrains_003FRider2025_002E1_003Fresharper_002Dhost_003FDecompilerCache_003Fdecompiler_003Fae7b55f2e79e4a30b19151f53aa9af29197600_003F08_003Fd04928fd_003FMainCharacterStatValueDisplay_002Ecs/@EntryIndexedValue">ForceIncluded</s:String>
|
||||||
|
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003AMainMenu_002Ecs_002Fl_003AC_0021_003FUsers_003FLenovo_003FAppData_003FRoaming_003FJetBrains_003FRider2025_002E1_003Fresharper_002Dhost_003FDecompilerCache_003Fdecompiler_003Fae7b55f2e79e4a30b19151f53aa9af29197600_003F21_003F0ec534a6_003FMainMenu_002Ecs/@EntryIndexedValue">ForceIncluded</s:String>
|
||||||
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003AModBehaviour_002Ecs_002Fl_003AC_0021_003FUsers_003FLenovo_003FAppData_003FRoaming_003FJetBrains_003FRider2025_002E1_003Fresharper_002Dhost_003FDecompilerCache_003Fdecompiler_003F15eaac0daac842bca117926c0c9be2781a00_003F37_003Fa09d99b2_003FModBehaviour_002Ecs_002Fz_003A4_002D3/@EntryIndexedValue">ForceIncluded</s:String>
|
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003AModBehaviour_002Ecs_002Fl_003AC_0021_003FUsers_003FLenovo_003FAppData_003FRoaming_003FJetBrains_003FRider2025_002E1_003Fresharper_002Dhost_003FDecompilerCache_003Fdecompiler_003F15eaac0daac842bca117926c0c9be2781a00_003F37_003Fa09d99b2_003FModBehaviour_002Ecs_002Fz_003A4_002D3/@EntryIndexedValue">ForceIncluded</s:String>
|
||||||
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003AModBehaviour_002Ecs_002Fl_003AC_0021_003FUsers_003FLenovo_003FAppData_003FRoaming_003FJetBrains_003FRider2025_002E1_003Fresharper_002Dhost_003FDecompilerCache_003Fdecompiler_003Fae7b55f2e79e4a30b19151f53aa9af29197600_003Ff3_003Ff5f2b19a_003FModBehaviour_002Ecs/@EntryIndexedValue">ForceIncluded</s:String>
|
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003AModBehaviour_002Ecs_002Fl_003AC_0021_003FUsers_003FLenovo_003FAppData_003FRoaming_003FJetBrains_003FRider2025_002E1_003Fresharper_002Dhost_003FDecompilerCache_003Fdecompiler_003Fae7b55f2e79e4a30b19151f53aa9af29197600_003Ff3_003Ff5f2b19a_003FModBehaviour_002Ecs/@EntryIndexedValue">ForceIncluded</s:String>
|
||||||
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003AModBehaviour_002Ecs_002Fl_003AC_0021_003FUsers_003FLenovo_003FAppData_003FRoaming_003FJetBrains_003FRider2025_002E1_003Fresharper_002Dhost_003FDecompilerCache_003Fdecompiler_003Faf3c3aa7ce14487c914dade28d96504e70a00_003F26_003Fe998e749_003FModBehaviour_002Ecs/@EntryIndexedValue">ForceIncluded</s:String>
|
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003AModBehaviour_002Ecs_002Fl_003AC_0021_003FUsers_003FLenovo_003FAppData_003FRoaming_003FJetBrains_003FRider2025_002E1_003Fresharper_002Dhost_003FDecompilerCache_003Fdecompiler_003Faf3c3aa7ce14487c914dade28d96504e70a00_003F26_003Fe998e749_003FModBehaviour_002Ecs/@EntryIndexedValue">ForceIncluded</s:String>
|
||||||
@@ -32,10 +44,17 @@
|
|||||||
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003AModInfo_002Ecs_002Fl_003AC_0021_003FUsers_003FLenovo_003FAppData_003FRoaming_003FJetBrains_003FRider2025_002E1_003Fresharper_002Dhost_003FDecompilerCache_003Fdecompiler_003Fae7b55f2e79e4a30b19151f53aa9af29197600_003F3c_003F9c6fe4cd_003FModInfo_002Ecs/@EntryIndexedValue">ForceIncluded</s:String>
|
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003AModInfo_002Ecs_002Fl_003AC_0021_003FUsers_003FLenovo_003FAppData_003FRoaming_003FJetBrains_003FRider2025_002E1_003Fresharper_002Dhost_003FDecompilerCache_003Fdecompiler_003Fae7b55f2e79e4a30b19151f53aa9af29197600_003F3c_003F9c6fe4cd_003FModInfo_002Ecs/@EntryIndexedValue">ForceIncluded</s:String>
|
||||||
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003AModManager_002Ecs_002Fl_003AC_0021_003FUsers_003FLenovo_003FAppData_003FRoaming_003FJetBrains_003FRider2025_002E1_003Fresharper_002Dhost_003FDecompilerCache_003Fdecompiler_003Fae7b55f2e79e4a30b19151f53aa9af29197600_003Fe5_003Ff729e34a_003FModManager_002Ecs/@EntryIndexedValue">ForceIncluded</s:String>
|
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003AModManager_002Ecs_002Fl_003AC_0021_003FUsers_003FLenovo_003FAppData_003FRoaming_003FJetBrains_003FRider2025_002E1_003Fresharper_002Dhost_003FDecompilerCache_003Fdecompiler_003Fae7b55f2e79e4a30b19151f53aa9af29197600_003Fe5_003Ff729e34a_003FModManager_002Ecs/@EntryIndexedValue">ForceIncluded</s:String>
|
||||||
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003AMonoBehaviour_002Ecs_002Fl_003AC_0021_003FUsers_003FLenovo_003FAppData_003FRoaming_003FJetBrains_003FRider2025_002E1_003Fresharper_002Dhost_003FDecompilerCache_003Fdecompiler_003Fc39a522eee05469b8171a6cfeb646c591543b0_003F02_003F65608a5e_003FMonoBehaviour_002Ecs/@EntryIndexedValue">ForceIncluded</s:String>
|
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003AMonoBehaviour_002Ecs_002Fl_003AC_0021_003FUsers_003FLenovo_003FAppData_003FRoaming_003FJetBrains_003FRider2025_002E1_003Fresharper_002Dhost_003FDecompilerCache_003Fdecompiler_003Fc39a522eee05469b8171a6cfeb646c591543b0_003F02_003F65608a5e_003FMonoBehaviour_002Ecs/@EntryIndexedValue">ForceIncluded</s:String>
|
||||||
|
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003AMultiInteractionMenu_002Ecs_002Fl_003AC_0021_003FUsers_003FLenovo_003FAppData_003FRoaming_003FJetBrains_003FRider2025_002E1_003Fresharper_002Dhost_003FDecompilerCache_003Fdecompiler_003Fae7b55f2e79e4a30b19151f53aa9af29197600_003F88_003F0d34c6e2_003FMultiInteractionMenu_002Ecs/@EntryIndexedValue">ForceIncluded</s:String>
|
||||||
|
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003AMultiInteraction_002Ecs_002Fl_003AC_0021_003FUsers_003FLenovo_003FAppData_003FRoaming_003FJetBrains_003FRider2025_002E1_003Fresharper_002Dhost_003FDecompilerCache_003Fdecompiler_003Fae7b55f2e79e4a30b19151f53aa9af29197600_003Ff8_003F995da332_003FMultiInteraction_002Ecs/@EntryIndexedValue">ForceIncluded</s:String>
|
||||||
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003AOcclusionFadeChecker_002Ecs_002Fl_003AC_0021_003FUsers_003FLenovo_003FAppData_003FRoaming_003FJetBrains_003FRider2025_002E1_003Fresharper_002Dhost_003FDecompilerCache_003Fdecompiler_003F492b6cef9eac4cd497c1ffcbfcb9161f197200_003F05_003F7aa60d94_003FOcclusionFadeChecker_002Ecs/@EntryIndexedValue">ForceIncluded</s:String>
|
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003AOcclusionFadeChecker_002Ecs_002Fl_003AC_0021_003FUsers_003FLenovo_003FAppData_003FRoaming_003FJetBrains_003FRider2025_002E1_003Fresharper_002Dhost_003FDecompilerCache_003Fdecompiler_003F492b6cef9eac4cd497c1ffcbfcb9161f197200_003F05_003F7aa60d94_003FOcclusionFadeChecker_002Ecs/@EntryIndexedValue">ForceIncluded</s:String>
|
||||||
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003AOcclusionFadeChecker_002Ecs_002Fl_003AC_0021_003FUsers_003FLenovo_003FAppData_003FRoaming_003FJetBrains_003FRider2025_002E1_003Fresharper_002Dhost_003FDecompilerCache_003Fdecompiler_003Fae7b55f2e79e4a30b19151f53aa9af29197600_003Fa8_003Fd12e1cf7_003FOcclusionFadeChecker_002Ecs/@EntryIndexedValue">ForceIncluded</s:String>
|
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003AOcclusionFadeChecker_002Ecs_002Fl_003AC_0021_003FUsers_003FLenovo_003FAppData_003FRoaming_003FJetBrains_003FRider2025_002E1_003Fresharper_002Dhost_003FDecompilerCache_003Fdecompiler_003Fae7b55f2e79e4a30b19151f53aa9af29197600_003Fa8_003Fd12e1cf7_003FOcclusionFadeChecker_002Ecs/@EntryIndexedValue">ForceIncluded</s:String>
|
||||||
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003AOcclusionFadeManager_002Ecs_002Fl_003AC_0021_003FUsers_003FLenovo_003FAppData_003FRoaming_003FJetBrains_003FRider2025_002E1_003Fresharper_002Dhost_003FDecompilerCache_003Fdecompiler_003F492b6cef9eac4cd497c1ffcbfcb9161f197200_003F70_003F6aba3c97_003FOcclusionFadeManager_002Ecs/@EntryIndexedValue">ForceIncluded</s:String>
|
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003AOcclusionFadeManager_002Ecs_002Fl_003AC_0021_003FUsers_003FLenovo_003FAppData_003FRoaming_003FJetBrains_003FRider2025_002E1_003Fresharper_002Dhost_003FDecompilerCache_003Fdecompiler_003F492b6cef9eac4cd497c1ffcbfcb9161f197200_003F70_003F6aba3c97_003FOcclusionFadeManager_002Ecs/@EntryIndexedValue">ForceIncluded</s:String>
|
||||||
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003AOptionsPanel_002Ecs_002Fl_003AC_0021_003FUsers_003FLenovo_003FAppData_003FRoaming_003FJetBrains_003FRider2025_002E1_003Fresharper_002Dhost_003FDecompilerCache_003Fdecompiler_003Fae7b55f2e79e4a30b19151f53aa9af29197600_003Fe1_003Fac8e1feb_003FOptionsPanel_002Ecs/@EntryIndexedValue">ForceIncluded</s:String>
|
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003AOptionsPanel_002Ecs_002Fl_003AC_0021_003FUsers_003FLenovo_003FAppData_003FRoaming_003FJetBrains_003FRider2025_002E1_003Fresharper_002Dhost_003FDecompilerCache_003Fdecompiler_003Fae7b55f2e79e4a30b19151f53aa9af29197600_003Fe1_003Fac8e1feb_003FOptionsPanel_002Ecs/@EntryIndexedValue">ForceIncluded</s:String>
|
||||||
|
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003APrefabPool_00601_002Ecs_002Fl_003AC_0021_003FUsers_003FLenovo_003FAppData_003FRoaming_003FJetBrains_003FRider2025_002E1_003Fresharper_002Dhost_003FDecompilerCache_003Fdecompiler_003F2fb2e03687cc4f6182420d823a41eca07200_003F05_003F8982f5f5_003FPrefabPool_00601_002Ecs/@EntryIndexedValue">ForceIncluded</s:String>
|
||||||
|
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003ASceneLoadingContext_002Ecs_002Fl_003AC_0021_003FUsers_003FLenovo_003FAppData_003FRoaming_003FJetBrains_003FRider2025_002E1_003Fresharper_002Dhost_003FDecompilerCache_003Fdecompiler_003Fae7b55f2e79e4a30b19151f53aa9af29197600_003Fbf_003F2b3732af_003FSceneLoadingContext_002Ecs/@EntryIndexedValue">ForceIncluded</s:String>
|
||||||
|
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003AScriptableObject_002Ecs_002Fl_003AC_0021_003FUsers_003FLenovo_003FAppData_003FRoaming_003FJetBrains_003FRider2025_002E1_003Fresharper_002Dhost_003FDecompilerCache_003Fdecompiler_003Fc39a522eee05469b8171a6cfeb646c591543b0_003F4d_003F1a33cb14_003FScriptableObject_002Ecs/@EntryIndexedValue">ForceIncluded</s:String>
|
||||||
|
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003ATitle_002Ecs_002Fl_003AC_0021_003FUsers_003FLenovo_003FAppData_003FRoaming_003FJetBrains_003FRider2025_002E1_003Fresharper_002Dhost_003FDecompilerCache_003Fdecompiler_003Fae7b55f2e79e4a30b19151f53aa9af29197600_003Fe8_003F42124cdd_003FTitle_002Ecs/@EntryIndexedValue">ForceIncluded</s:String>
|
||||||
|
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003AUIPrefabsReference_002Ecs_002Fl_003AC_0021_003FUsers_003FLenovo_003FAppData_003FRoaming_003FJetBrains_003FRider2025_002E1_003Fresharper_002Dhost_003FDecompilerCache_003Fdecompiler_003Fae7b55f2e79e4a30b19151f53aa9af29197600_003Fb5_003F49d2159e_003FUIPrefabsReference_002Ecs/@EntryIndexedValue">ForceIncluded</s:String>
|
||||||
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003AVolumeComponent_002Ecs_002Fl_003AC_0021_003FUsers_003FLenovo_003FAppData_003FRoaming_003FJetBrains_003FRider2025_002E1_003Fresharper_002Dhost_003FDecompilerCache_003Fdecompiler_003Fb02e352b027d497b8571da05e536c2f682000_003F36_003F74cee2c4_003FVolumeComponent_002Ecs/@EntryIndexedValue">ForceIncluded</s:String>
|
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003AVolumeComponent_002Ecs_002Fl_003AC_0021_003FUsers_003FLenovo_003FAppData_003FRoaming_003FJetBrains_003FRider2025_002E1_003Fresharper_002Dhost_003FDecompilerCache_003Fdecompiler_003Fb02e352b027d497b8571da05e536c2f682000_003F36_003F74cee2c4_003FVolumeComponent_002Ecs/@EntryIndexedValue">ForceIncluded</s:String>
|
||||||
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003AVolume_002Ecs_002Fl_003AC_0021_003FUsers_003FLenovo_003FAppData_003FRoaming_003FJetBrains_003FRider2025_002E1_003Fresharper_002Dhost_003FDecompilerCache_003Fdecompiler_003Fb02e352b027d497b8571da05e536c2f682000_003Ffa_003F02e0206c_003FVolume_002Ecs/@EntryIndexedValue">ForceIncluded</s:String>
|
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003AVolume_002Ecs_002Fl_003AC_0021_003FUsers_003FLenovo_003FAppData_003FRoaming_003FJetBrains_003FRider2025_002E1_003Fresharper_002Dhost_003FDecompilerCache_003Fdecompiler_003Fb02e352b027d497b8571da05e536c2f682000_003Ffa_003F02e0206c_003FVolume_002Ecs/@EntryIndexedValue">ForceIncluded</s:String>
|
||||||
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003A_005F003CModule_005F003E_002Ecs_002Fl_003AC_0021_003FUsers_003FLenovo_003FAppData_003FRoaming_003FJetBrains_003FRider2025_002E1_003Fresharper_002Dhost_003FDecompilerCache_003Fdecompiler_003Faf3c3aa7ce14487c914dade28d96504e70a00_003F9c_003Ff3cdecda_003F_005F003CModule_005F003E_002Ecs/@EntryIndexedValue">ForceIncluded</s:String>
|
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003A_005F003CModule_005F003E_002Ecs_002Fl_003AC_0021_003FUsers_003FLenovo_003FAppData_003FRoaming_003FJetBrains_003FRider2025_002E1_003Fresharper_002Dhost_003FDecompilerCache_003Fdecompiler_003Faf3c3aa7ce14487c914dade28d96504e70a00_003F9c_003Ff3cdecda_003F_005F003CModule_005F003E_002Ecs/@EntryIndexedValue">ForceIncluded</s:String>
|
||||||
|
|||||||
@@ -13,7 +13,7 @@ using System.Reflection;
|
|||||||
[assembly: System.Reflection.AssemblyCompanyAttribute("折纸的小箱子")]
|
[assembly: System.Reflection.AssemblyCompanyAttribute("折纸的小箱子")]
|
||||||
[assembly: System.Reflection.AssemblyConfigurationAttribute("Release")]
|
[assembly: System.Reflection.AssemblyConfigurationAttribute("Release")]
|
||||||
[assembly: System.Reflection.AssemblyFileVersionAttribute("1.0.1")]
|
[assembly: System.Reflection.AssemblyFileVersionAttribute("1.0.1")]
|
||||||
[assembly: System.Reflection.AssemblyInformationalVersionAttribute("1.0.1+0206a83f56b5a794fe2f173b4a047cc4f0d4cd90")]
|
[assembly: System.Reflection.AssemblyInformationalVersionAttribute("1.0.1+5d69efbc3f80a5422cef0884e02fb27adf20b467")]
|
||||||
[assembly: System.Reflection.AssemblyProductAttribute("HideCharacter")]
|
[assembly: System.Reflection.AssemblyProductAttribute("HideCharacter")]
|
||||||
[assembly: System.Reflection.AssemblyTitleAttribute("HideCharacter")]
|
[assembly: System.Reflection.AssemblyTitleAttribute("HideCharacter")]
|
||||||
[assembly: System.Reflection.AssemblyVersionAttribute("1.0.1")]
|
[assembly: System.Reflection.AssemblyVersionAttribute("1.0.1")]
|
||||||
|
|||||||
@@ -1 +1 @@
|
|||||||
0c41c29df034d7303af5a922ba9d31f37af5b0419be39221c78f9e624f5d890a
|
769c2595fb11f290049896c9b627f3b39daef1dc9f833f4947c915142e78d8d2
|
||||||
|
|||||||
@@ -1 +1 @@
|
|||||||
17619928534493660
|
17623343068138064
|
||||||
488
HitFeedback/Api/ModConfigApi.cs
Normal file
488
HitFeedback/Api/ModConfigApi.cs
Normal file
@@ -0,0 +1,488 @@
|
|||||||
|
using System;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Reflection;
|
||||||
|
using UnityEngine;
|
||||||
|
|
||||||
|
//替换为你的mod命名空间, 防止多个同名ModConfigAPI冲突
|
||||||
|
namespace HitFeedback.Api {
|
||||||
|
/// <summary>
|
||||||
|
/// ModConfig 安全接口封装类 - 提供不抛异常的静态接口
|
||||||
|
/// ModConfig Safe API Wrapper Class - Provides non-throwing static interfaces
|
||||||
|
/// </summary>
|
||||||
|
public static class ModConfigAPI
|
||||||
|
{
|
||||||
|
public static string ModConfigName = "ModConfig";
|
||||||
|
|
||||||
|
//Ensure this match the number of ModConfig.ModBehaviour.VERSION
|
||||||
|
//这里确保版本号与ModConfig.ModBehaviour.VERSION匹配
|
||||||
|
private const int ModConfigVersion = 1;
|
||||||
|
|
||||||
|
private static string TAG = $"ModConfig_v{ModConfigVersion}";
|
||||||
|
|
||||||
|
private static Type modBehaviourType;
|
||||||
|
private static Type optionsManagerType;
|
||||||
|
public static bool isInitialized = false;
|
||||||
|
private static bool versionChecked = false;
|
||||||
|
private static bool isVersionCompatible = false;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 检查版本兼容性
|
||||||
|
/// Check version compatibility
|
||||||
|
/// </summary>
|
||||||
|
private static bool CheckVersionCompatibility()
|
||||||
|
{
|
||||||
|
if (versionChecked)
|
||||||
|
return isVersionCompatible;
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
// 尝试获取 ModConfig 的版本号
|
||||||
|
// Try to get ModConfig version number
|
||||||
|
var versionField = modBehaviourType.GetField("VERSION", BindingFlags.Public | BindingFlags.Static);
|
||||||
|
if (versionField != null && versionField.FieldType == typeof(int))
|
||||||
|
{
|
||||||
|
var modConfigVersion = (int)versionField.GetValue(null);
|
||||||
|
isVersionCompatible = (modConfigVersion == ModConfigVersion);
|
||||||
|
|
||||||
|
if (!isVersionCompatible)
|
||||||
|
{
|
||||||
|
Debug.LogError($"[{TAG}] 版本不匹配!API版本: {ModConfigVersion}, ModConfig版本: {modConfigVersion}");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
Debug.Log($"[{TAG}] 版本检查通过: {ModConfigVersion}");
|
||||||
|
versionChecked = true;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// 如果找不到版本字段,发出警告但继续运行(向后兼容)
|
||||||
|
// If version field not found, warn but continue (backward compatibility)
|
||||||
|
Debug.LogWarning($"[{TAG}] 未找到版本信息字段,跳过版本检查");
|
||||||
|
isVersionCompatible = true;
|
||||||
|
versionChecked = true;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
Debug.LogError($"[{TAG}] 版本检查失败: {ex.Message}");
|
||||||
|
isVersionCompatible = false;
|
||||||
|
versionChecked = true;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 初始化 ModConfigAPI,检查必要的函数是否存在
|
||||||
|
/// Initialize ModConfigAPI, check if necessary functions exist
|
||||||
|
/// </summary>
|
||||||
|
public static bool Initialize()
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
if (isInitialized)
|
||||||
|
return true;
|
||||||
|
|
||||||
|
// 获取 ModBehaviour 类型
|
||||||
|
// Get ModBehaviour type
|
||||||
|
modBehaviourType = FindTypeInAssemblies("ModConfig.ModBehaviour");
|
||||||
|
if (modBehaviourType == null)
|
||||||
|
{
|
||||||
|
Debug.LogWarning($"[{TAG}] ModConfig.ModBehaviour 类型未找到,ModConfig 可能未加载");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 获取 OptionsManager_Mod 类型
|
||||||
|
// Get OptionsManager_Mod type
|
||||||
|
optionsManagerType = FindTypeInAssemblies("ModConfig.OptionsManager_Mod");
|
||||||
|
if (optionsManagerType == null)
|
||||||
|
{
|
||||||
|
Debug.LogWarning($"[{TAG}] ModConfig.OptionsManager_Mod 类型未找到");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 检查版本兼容性
|
||||||
|
// Check version compatibility
|
||||||
|
if (!CheckVersionCompatibility())
|
||||||
|
{
|
||||||
|
Debug.LogWarning($"[{TAG}] ModConfig version mismatch!!!");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 检查必要的静态方法是否存在
|
||||||
|
// Check if necessary static methods exist
|
||||||
|
string[] requiredMethods = {
|
||||||
|
"AddDropdownList",
|
||||||
|
"AddInputWithSlider",
|
||||||
|
"AddBoolDropdownList",
|
||||||
|
"AddOnOptionsChangedDelegate",
|
||||||
|
"RemoveOnOptionsChangedDelegate",
|
||||||
|
};
|
||||||
|
|
||||||
|
foreach (var methodName in requiredMethods)
|
||||||
|
{
|
||||||
|
var method = modBehaviourType.GetMethod(methodName, BindingFlags.Public | BindingFlags.Static);
|
||||||
|
if (method == null)
|
||||||
|
{
|
||||||
|
Debug.LogError($"[{TAG}] 必要方法 {methodName} 未找到");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
isInitialized = true;
|
||||||
|
Debug.Log($"[{TAG}] ModConfigAPI 初始化成功");
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
Debug.LogError($"[{TAG}] 初始化失败: {ex.Message}");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 在所有已加载的程序集中查找类型
|
||||||
|
/// </summary>
|
||||||
|
private static Type FindTypeInAssemblies(string typeName)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
// 获取当前域中的所有程序集
|
||||||
|
Assembly[] assemblies = AppDomain.CurrentDomain.GetAssemblies();
|
||||||
|
|
||||||
|
foreach (var assembly in assemblies)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
// 检查程序集名称是否包含 ModConfig
|
||||||
|
if (assembly.FullName.Contains("ModConfig"))
|
||||||
|
{
|
||||||
|
Debug.Log($"[{TAG}] 找到 ModConfig 相关程序集: {assembly.FullName}");
|
||||||
|
}
|
||||||
|
|
||||||
|
// 尝试在该程序集中查找类型
|
||||||
|
var type = assembly.GetType(typeName);
|
||||||
|
if (type != null)
|
||||||
|
{
|
||||||
|
Debug.Log($"[{TAG}] 在程序集 {assembly.FullName} 中找到类型 {typeName}");
|
||||||
|
return type;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
// 忽略单个程序集的查找错误
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 记录所有已加载的程序集用于调试
|
||||||
|
Debug.LogWarning($"[{TAG}] 在所有程序集中未找到类型 {typeName},已加载程序集数量: {assemblies.Length}");
|
||||||
|
foreach (var assembly in assemblies.Where(a => a.FullName.Contains("ModConfig")))
|
||||||
|
{
|
||||||
|
Debug.Log($"[{TAG}] ModConfig 相关程序集: {assembly.FullName}");
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
Debug.LogError($"[{TAG}] 程序集扫描失败: {ex.Message}");
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 安全地添加选项变更事件委托
|
||||||
|
/// Safely add options changed event delegate
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="action">事件处理委托,参数为变更的选项键名</param>
|
||||||
|
/// <returns>是否成功添加</returns>
|
||||||
|
public static bool SafeAddOnOptionsChangedDelegate(Action<string> action)
|
||||||
|
{
|
||||||
|
if (!Initialize())
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if (action == null)
|
||||||
|
{
|
||||||
|
Debug.LogWarning($"[{TAG}] 不能添加空的事件委托");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var method = modBehaviourType.GetMethod("AddOnOptionsChangedDelegate", BindingFlags.Public | BindingFlags.Static);
|
||||||
|
method.Invoke(null, new object[] { action });
|
||||||
|
|
||||||
|
Debug.Log($"[{TAG}] 成功添加选项变更事件委托");
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
Debug.LogError($"[{TAG}] 添加选项变更事件委托失败: {ex.Message}");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 安全地移除选项变更事件委托
|
||||||
|
/// Safely remove options changed event delegate
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="action">要移除的事件处理委托</param>
|
||||||
|
/// <returns>是否成功移除</returns>
|
||||||
|
public static bool SafeRemoveOnOptionsChangedDelegate(Action<string> action)
|
||||||
|
{
|
||||||
|
if (!Initialize())
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if (action == null)
|
||||||
|
{
|
||||||
|
Debug.LogWarning($"[{TAG}] 不能移除空的事件委托");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var method = modBehaviourType.GetMethod("RemoveOnOptionsChangedDelegate", BindingFlags.Public | BindingFlags.Static);
|
||||||
|
method.Invoke(null, new object[] { action });
|
||||||
|
|
||||||
|
Debug.Log($"[{TAG}] 成功移除选项变更事件委托");
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
Debug.LogError($"[{TAG}] 移除选项变更事件委托失败: {ex.Message}");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 安全地添加下拉列表配置项
|
||||||
|
/// Safely add dropdown list configuration item
|
||||||
|
/// </summary>
|
||||||
|
public static bool SafeAddDropdownList(string modName, string key, string description, System.Collections.Generic.SortedDictionary<string, object> options, Type valueType, object defaultValue)
|
||||||
|
{
|
||||||
|
key = $"{modName}_{key}";
|
||||||
|
|
||||||
|
if (!Initialize())
|
||||||
|
return false;
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var method = modBehaviourType.GetMethod("AddDropdownList", BindingFlags.Public | BindingFlags.Static);
|
||||||
|
method.Invoke(null, new object[] { modName, key, description, options, valueType, defaultValue });
|
||||||
|
|
||||||
|
Debug.Log($"[{TAG}] 成功添加下拉列表: {modName}.{key}");
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
Debug.LogError($"[{TAG}] 添加下拉列表失败 {modName}.{key}: {ex.Message}");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 安全地添加带滑条的输入框配置项
|
||||||
|
/// Safely add input box with slider configuration item
|
||||||
|
/// </summary>
|
||||||
|
public static bool SafeAddInputWithSlider(string modName, string key, string description, Type valueType, object defaultValue, UnityEngine.Vector2? sliderRange = null)
|
||||||
|
{
|
||||||
|
key = $"{modName}_{key}";
|
||||||
|
|
||||||
|
if (!Initialize())
|
||||||
|
return false;
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var method = modBehaviourType.GetMethod("AddInputWithSlider", BindingFlags.Public | BindingFlags.Static);
|
||||||
|
|
||||||
|
// 处理可空参数
|
||||||
|
// Handle nullable parameters
|
||||||
|
var parameters = sliderRange.HasValue ?
|
||||||
|
new object[] { modName, key, description, valueType, defaultValue, sliderRange.Value } :
|
||||||
|
new object[] { modName, key, description, valueType, defaultValue, null };
|
||||||
|
|
||||||
|
method.Invoke(null, parameters);
|
||||||
|
|
||||||
|
Debug.Log($"[{TAG}] 成功添加滑条输入框: {modName}.{key}");
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
Debug.LogError($"[{TAG}] 添加滑条输入框失败 {modName}.{key}: {ex.Message}");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 安全地添加布尔下拉列表配置项
|
||||||
|
/// Safely add boolean dropdown list configuration item
|
||||||
|
/// </summary>
|
||||||
|
public static bool SafeAddBoolDropdownList(string modName, string key, string description, bool defaultValue)
|
||||||
|
{
|
||||||
|
key = $"{modName}_{key}";
|
||||||
|
|
||||||
|
if (!Initialize())
|
||||||
|
return false;
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var method = modBehaviourType.GetMethod("AddBoolDropdownList", BindingFlags.Public | BindingFlags.Static);
|
||||||
|
method.Invoke(null, new object[] { modName, key, description, defaultValue });
|
||||||
|
|
||||||
|
Debug.Log($"[{TAG}] 成功添加布尔下拉列表: {modName}.{key}");
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
Debug.LogError($"[{TAG}] 添加布尔下拉列表失败 {modName}.{key}: {ex.Message}");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 安全地加载配置值
|
||||||
|
/// Safely load configuration value
|
||||||
|
/// </summary>
|
||||||
|
/// <typeparam name="T">值的类型</typeparam>
|
||||||
|
/// <param name="key">配置键</param>
|
||||||
|
/// <param name="defaultValue">默认值</param>
|
||||||
|
/// <returns>加载的值或默认值</returns>
|
||||||
|
public static T SafeLoad<T>(string mod_name, string key, T defaultValue = default(T))
|
||||||
|
{
|
||||||
|
key = $"{mod_name}_{key}";
|
||||||
|
|
||||||
|
if (!Initialize())
|
||||||
|
return defaultValue;
|
||||||
|
|
||||||
|
if (string.IsNullOrEmpty(key))
|
||||||
|
{
|
||||||
|
Debug.LogWarning($"[{TAG}] 配置键不能为空");
|
||||||
|
return defaultValue;
|
||||||
|
}
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var loadMethod = optionsManagerType.GetMethod("Load", BindingFlags.Public | BindingFlags.Static);
|
||||||
|
if (loadMethod == null)
|
||||||
|
{
|
||||||
|
Debug.LogError($"[{TAG}] 未找到 OptionsManager_Mod.Load 方法");
|
||||||
|
return defaultValue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 获取泛型方法
|
||||||
|
var genericLoadMethod = loadMethod.MakeGenericMethod(typeof(T));
|
||||||
|
var result = genericLoadMethod.Invoke(null, new object[] { key, defaultValue });
|
||||||
|
|
||||||
|
Debug.Log($"[{TAG}] 成功加载配置: {key} = {result}");
|
||||||
|
return (T)result;
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
Debug.LogError($"[{TAG}] 加载配置失败 {key}: {ex.Message}");
|
||||||
|
return defaultValue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 安全地保存配置值
|
||||||
|
/// Safely save configuration value
|
||||||
|
/// </summary>
|
||||||
|
/// <typeparam name="T">值的类型</typeparam>
|
||||||
|
/// <param name="key">配置键</param>
|
||||||
|
/// <param name="value">要保存的值</param>
|
||||||
|
/// <returns>是否保存成功</returns>
|
||||||
|
public static bool SafeSave<T>(string mod_name, string key, T value)
|
||||||
|
{
|
||||||
|
key = $"{mod_name}_{key}";
|
||||||
|
|
||||||
|
if (!Initialize())
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if (string.IsNullOrEmpty(key))
|
||||||
|
{
|
||||||
|
Debug.LogWarning($"[{TAG}] 配置键不能为空");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var saveMethod = optionsManagerType.GetMethod("Save", BindingFlags.Public | BindingFlags.Static);
|
||||||
|
if (saveMethod == null)
|
||||||
|
{
|
||||||
|
Debug.LogError($"[{TAG}] 未找到 OptionsManager_Mod.Save 方法");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 获取泛型方法
|
||||||
|
var genericSaveMethod = saveMethod.MakeGenericMethod(typeof(T));
|
||||||
|
genericSaveMethod.Invoke(null, new object[] { key, value });
|
||||||
|
|
||||||
|
Debug.Log($"[{TAG}] 成功保存配置: {key} = {value}");
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
Debug.LogError($"[{TAG}] 保存配置失败 {key}: {ex.Message}");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 检查 ModConfig 是否可用
|
||||||
|
/// Check if ModConfig is available
|
||||||
|
/// </summary>
|
||||||
|
public static bool IsAvailable()
|
||||||
|
{
|
||||||
|
return Initialize();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 获取 ModConfig 版本信息(如果存在)
|
||||||
|
/// Get ModConfig version information (if exists)
|
||||||
|
/// </summary>
|
||||||
|
public static string GetVersionInfo()
|
||||||
|
{
|
||||||
|
if (!Initialize())
|
||||||
|
return "ModConfig 未加载 | ModConfig not loaded";
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
// 尝试获取版本信息(如果 ModBehaviour 有相关字段或属性)
|
||||||
|
// Try to get version information (if ModBehaviour has related fields or properties)
|
||||||
|
var versionField = modBehaviourType.GetField("VERSION", BindingFlags.Public | BindingFlags.Static);
|
||||||
|
if (versionField != null && versionField.FieldType == typeof(int))
|
||||||
|
{
|
||||||
|
var modConfigVersion = (int)versionField.GetValue(null);
|
||||||
|
var compatibility = (modConfigVersion == ModConfigVersion) ? "兼容" : "不兼容";
|
||||||
|
return $"ModConfig v{modConfigVersion} (API v{ModConfigVersion}, {compatibility})";
|
||||||
|
}
|
||||||
|
|
||||||
|
var versionProperty = modBehaviourType.GetProperty("VERSION", BindingFlags.Public | BindingFlags.Static);
|
||||||
|
if (versionProperty != null)
|
||||||
|
{
|
||||||
|
var versionValue = versionProperty.GetValue(null);
|
||||||
|
return versionValue?.ToString() ?? "未知版本 | Unknown version";
|
||||||
|
}
|
||||||
|
|
||||||
|
return "ModConfig 已加载(版本信息不可用) | ModConfig loaded (version info unavailable)";
|
||||||
|
}
|
||||||
|
catch
|
||||||
|
{
|
||||||
|
return "ModConfig 已加载(版本检查失败) | ModConfig loaded (version check failed)";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 检查版本兼容性
|
||||||
|
/// Check version compatibility
|
||||||
|
/// </summary>
|
||||||
|
public static bool IsVersionCompatible()
|
||||||
|
{
|
||||||
|
if (!Initialize())
|
||||||
|
return false;
|
||||||
|
return isVersionCompatible;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,4 +1,6 @@
|
|||||||
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
using System.Globalization;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using UnityEngine; // 假设在Unity环境中使用Debug.LogError
|
using UnityEngine; // 假设在Unity环境中使用Debug.LogError
|
||||||
|
|
||||||
@@ -9,78 +11,140 @@ namespace HitFeedback
|
|||||||
public KeyCode hotKey = KeyCode.F8;
|
public KeyCode hotKey = KeyCode.F8;
|
||||||
public Dictionary<string, float> probability = new Dictionary<string, float>();
|
public Dictionary<string, float> probability = new Dictionary<string, float>();
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 当伤害具有这些特性之一时,将播放音频反馈。
|
||||||
|
/// </summary>
|
||||||
|
public HashSet<DamageFeature> audioDamageFeatures = new HashSet<DamageFeature>();
|
||||||
|
|
||||||
public void LoadConfig(string filename)
|
public void LoadConfig(string filename)
|
||||||
{
|
{
|
||||||
if (!File.Exists(filename))
|
if (!File.Exists(filename))
|
||||||
{
|
{
|
||||||
Debug.LogError($"Config file not found: {filename}");
|
Debug.LogError($"Config file not found: {filename}");
|
||||||
return; // 如果文件不存在,就没必要继续了
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// 清空旧的概率数据,确保每次加载都是从新开始
|
|
||||||
probability.Clear();
|
probability.Clear();
|
||||||
|
audioDamageFeatures.Clear(); // 清空旧的音频伤害特性数据
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
using (var sr = new StreamReader(filename))
|
using (var sr = new StreamReader(filename))
|
||||||
{
|
{
|
||||||
string line;
|
string line;
|
||||||
var lineNumber = 0; // 用于错误报告
|
var lineNumber = 0;
|
||||||
|
string currentSection = ""; // 用于解析节
|
||||||
while ((line = sr.ReadLine()) != null)
|
while ((line = sr.ReadLine()) != null)
|
||||||
{
|
{
|
||||||
lineNumber++;
|
lineNumber++;
|
||||||
line = line.Trim(); // 移除行首尾的空白字符
|
line = line.Trim();
|
||||||
|
|
||||||
// 忽略空行和注释行
|
// 忽略空行和注释行
|
||||||
if (string.IsNullOrEmpty(line) || line.StartsWith(";") || line.StartsWith("#"))
|
if (string.IsNullOrEmpty(line) || line.StartsWith(";") || line.StartsWith("#"))
|
||||||
{
|
{
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 处理节标题,例如 [General] 或 [AudioFeatures]
|
||||||
|
if (line.StartsWith("[") && line.EndsWith("]"))
|
||||||
|
{
|
||||||
|
currentSection = line.Substring(1, line.Length - 2).Trim();
|
||||||
|
continue; // 跳过节标题行
|
||||||
|
}
|
||||||
|
|
||||||
// 查找等号
|
// 查找等号
|
||||||
var separatorIndex = line.IndexOf('=');
|
var separatorIndex = line.IndexOf('=');
|
||||||
if (separatorIndex == -1)
|
if (separatorIndex == -1)
|
||||||
{
|
{
|
||||||
Debug.LogWarning($"Skipping malformed line in config file '{filename}' at line {lineNumber}: No '=' found. Line: '{line}'");
|
Debug.LogWarning(
|
||||||
|
$"Skipping malformed line in config file '{filename}' at line {lineNumber}: No '=' found. Line: '{line}'");
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
var key = line.Substring(0, separatorIndex).Trim();
|
var key = line.Substring(0, separatorIndex).Trim();
|
||||||
var valueStr = line.Substring(separatorIndex + 1).Trim();
|
var valueStr = line.Substring(separatorIndex + 1).Trim();
|
||||||
|
if (currentSection.Equals("General", StringComparison.OrdinalIgnoreCase))
|
||||||
// 解析 hotKey
|
|
||||||
if (key.Equals("hotKey", System.StringComparison.OrdinalIgnoreCase))
|
|
||||||
{
|
{
|
||||||
try
|
// 解析 hotKey
|
||||||
|
if (key.Equals("hotKey", StringComparison.OrdinalIgnoreCase))
|
||||||
{
|
{
|
||||||
hotKey = (KeyCode)System.Enum.Parse(typeof(KeyCode), valueStr, true);
|
try
|
||||||
}
|
{
|
||||||
catch (System.ArgumentException)
|
hotKey = (KeyCode)Enum.Parse(typeof(KeyCode), valueStr, true);
|
||||||
{
|
}
|
||||||
Debug.LogError($"Invalid KeyCode '{valueStr}' in config file '{filename}' at line {lineNumber}. Using default F8.");
|
catch (ArgumentException)
|
||||||
hotKey = KeyCode.F8; // 设置为默认值或保持不变
|
{
|
||||||
|
Debug.LogError(
|
||||||
|
$"Invalid KeyCode '{valueStr}' in config file '{filename}' at line {lineNumber}. Using default F8.");
|
||||||
|
hotKey = KeyCode.F8;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// 解析 probability 字典项
|
else if (currentSection.Equals("Probabilities", StringComparison.OrdinalIgnoreCase))
|
||||||
else
|
|
||||||
{
|
{
|
||||||
if (float.TryParse(valueStr, out var probValue))
|
// 解析 probability 字典项
|
||||||
|
if (float.TryParse(valueStr, NumberStyles.Float, CultureInfo.InvariantCulture,
|
||||||
|
out var probValue))
|
||||||
{
|
{
|
||||||
probability[key] = probValue;
|
probability[key] = probValue;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
Debug.LogWarning($"Invalid float value '{valueStr}' for key '{key}' in config file '{filename}' at line {lineNumber}. Skipping entry.");
|
Debug.LogWarning(
|
||||||
|
$"Invalid float value '{valueStr}' for key '{key}' in config file '{filename}' at line {lineNumber}. Skipping entry.");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
else if (currentSection.Equals("AudioFeatures", StringComparison.OrdinalIgnoreCase))
|
||||||
|
{
|
||||||
|
// 解析 audioDamageFeatures 集合项
|
||||||
|
// 键是 'feature' (或者可以随意定义,只要值是我们关心的)
|
||||||
|
// 值是 ExtendedDamageFeature 的枚举名称
|
||||||
|
if (key.Equals("feature", StringComparison.OrdinalIgnoreCase)) // 假设所有特征都用同一个键"feature"
|
||||||
|
{
|
||||||
|
foreach (var featureName in valueStr.Split(new char[] { ',', '|' },
|
||||||
|
StringSplitOptions.RemoveEmptyEntries))
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
// 确保 ExtendedDamageFeature 是 [Flags] 枚举
|
||||||
|
var feature = (DamageFeature)Enum.Parse(typeof(DamageFeature),
|
||||||
|
featureName.Trim(), true);
|
||||||
|
audioDamageFeatures.Add(feature);
|
||||||
|
}
|
||||||
|
catch (ArgumentException)
|
||||||
|
{
|
||||||
|
Debug.LogWarning(
|
||||||
|
$"Invalid ExtendedDamageFeature '{featureName.Trim()}' in config file '{filename}' at line {lineNumber}. Skipping entry.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var feature =
|
||||||
|
(DamageFeature)Enum.Parse(typeof(DamageFeature), key, true);
|
||||||
|
if (bool.TryParse(valueStr, out bool includeFeature) && includeFeature)
|
||||||
|
{
|
||||||
|
audioDamageFeatures.Add(feature);
|
||||||
|
}
|
||||||
|
// 如果是 false,则不添加到集合,或者可以从集合中移除(如果默认是都包含)
|
||||||
|
}
|
||||||
|
catch (ArgumentException)
|
||||||
|
{
|
||||||
|
Debug.LogWarning(
|
||||||
|
$"Invalid ExtendedDamageFeature key '{key}' in config file '{filename}' at line {lineNumber}. Skipping entry.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// 如果存在其他节,可以在这里添加 else if 处理
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
catch (System.Exception ex)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
Debug.LogError($"Error reading config file '{filename}': {ex.Message}");
|
Debug.LogError($"Error reading config file '{filename}': {ex.Message}");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 将当前配置存储到指定INI文件。
|
/// 将当前配置存储到指定INI文件。
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@@ -89,17 +153,17 @@ namespace HitFeedback
|
|||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
// 确保目录存在
|
|
||||||
var directory = Path.GetDirectoryName(filename);
|
var directory = Path.GetDirectoryName(filename);
|
||||||
if (!string.IsNullOrEmpty(directory) && !Directory.Exists(directory))
|
if (!string.IsNullOrEmpty(directory) && !Directory.Exists(directory))
|
||||||
{
|
{
|
||||||
Directory.CreateDirectory(directory);
|
Directory.CreateDirectory(directory);
|
||||||
}
|
}
|
||||||
|
|
||||||
using (var sw = new StreamWriter(filename))
|
using (var sw = new StreamWriter(filename))
|
||||||
{
|
{
|
||||||
sw.WriteLine("; HitFeedback Configuration File");
|
sw.WriteLine("; HitFeedback Configuration File");
|
||||||
sw.WriteLine("; Generated by HitFeedback.Config class");
|
sw.WriteLine("; Generated by HitFeedback.Config class");
|
||||||
sw.WriteLine(); // 空行
|
sw.WriteLine();
|
||||||
sw.WriteLine("[General]");
|
sw.WriteLine("[General]");
|
||||||
sw.WriteLine($"hotKey = {hotKey.ToString()}");
|
sw.WriteLine($"hotKey = {hotKey.ToString()}");
|
||||||
sw.WriteLine();
|
sw.WriteLine();
|
||||||
@@ -108,22 +172,113 @@ namespace HitFeedback
|
|||||||
sw.WriteLine("[Probabilities]");
|
sw.WriteLine("[Probabilities]");
|
||||||
foreach (var kvp in probability)
|
foreach (var kvp in probability)
|
||||||
{
|
{
|
||||||
// 使用 InvariantCulture 确保浮点数的格式在不同区域设置下都一致
|
sw.WriteLine($"{kvp.Key} = {kvp.Value.ToString(CultureInfo.InvariantCulture)}");
|
||||||
sw.WriteLine($"{kvp.Key} = {kvp.Value.ToString(System.Globalization.CultureInfo.InvariantCulture)}");
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
sw.WriteLine("; No probabilities currently configured.");
|
sw.WriteLine("; No probabilities currently configured.");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
sw.WriteLine();
|
||||||
|
if (audioDamageFeatures.Count > 0)
|
||||||
|
{
|
||||||
|
sw.WriteLine("[AudioFeatures]");
|
||||||
|
// 假设每个特性一行,值为 true
|
||||||
|
foreach (var feature in audioDamageFeatures)
|
||||||
|
{
|
||||||
|
sw.WriteLine($"{feature.ToString()} = True");
|
||||||
|
}
|
||||||
|
// 或者如果你想用一个键存储所有特性(用逗号或竖线分隔)
|
||||||
|
// sw.WriteLine($"features = {string.Join(",", audioDamageFeatures.Select(f => f.ToString()))}");
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
sw.WriteLine("; No audio damage features currently configured.");
|
||||||
|
}
|
||||||
|
|
||||||
sw.WriteLine();
|
sw.WriteLine();
|
||||||
}
|
}
|
||||||
|
|
||||||
Debug.Log($"Config saved to: {filename}");
|
Debug.Log($"Config saved to: {filename}");
|
||||||
}
|
}
|
||||||
catch (System.Exception ex)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
Debug.LogError($"Error saving config file '{filename}': {ex.Message}");
|
Debug.LogError($"Error saving config file '{filename}': {ex.Message}");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
/// <summary>
|
||||||
|
/// 根据DamageInfo的特性判断是否应该播放音频反馈。
|
||||||
|
/// 如果DamageInfo的任何一个特性在audioDamageFeatures集合中,则返回true。
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="damageInfo">要检查的DamageInfo对象。</param>
|
||||||
|
/// <returns>如果应该播放音频反馈,则为true;否则为false。</returns>
|
||||||
|
public bool ShouldPlayAudioFeedback(DamageInfo damageInfo)
|
||||||
|
{
|
||||||
|
if (audioDamageFeatures.Count == 0)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
// 将DamageInfo的各种布尔属性/条件转换为DamageFeature组合
|
||||||
|
DamageFeature currentDamageFeatures = DamageFeature.Undefined;
|
||||||
|
if (damageInfo.damageType == DamageTypes.normal)
|
||||||
|
{
|
||||||
|
currentDamageFeatures |= DamageFeature.NormalDamage;
|
||||||
|
}
|
||||||
|
else if (damageInfo.damageType == DamageTypes.realDamage)
|
||||||
|
{
|
||||||
|
currentDamageFeatures |= DamageFeature.RealDamage;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (damageInfo.isFromBuffOrEffect)
|
||||||
|
{
|
||||||
|
currentDamageFeatures |= DamageFeature.BuffOrEffectDamage;
|
||||||
|
}
|
||||||
|
if (damageInfo.ignoreArmor)
|
||||||
|
{
|
||||||
|
currentDamageFeatures |= DamageFeature.ArmorIgnoringDamage;
|
||||||
|
}
|
||||||
|
if (damageInfo.crit > 0) // crit > 0 表示是暴击
|
||||||
|
{
|
||||||
|
currentDamageFeatures |= DamageFeature.CriticalDamage;
|
||||||
|
}
|
||||||
|
if (damageInfo.armorPiercing > 0)
|
||||||
|
{
|
||||||
|
currentDamageFeatures |= DamageFeature.ArmorPiercingDamage;
|
||||||
|
}
|
||||||
|
if (damageInfo.isExplosion)
|
||||||
|
{
|
||||||
|
currentDamageFeatures |= DamageFeature.ExplosionDamage;
|
||||||
|
}
|
||||||
|
if (damageInfo.armorBreak > 0)
|
||||||
|
{
|
||||||
|
currentDamageFeatures |= DamageFeature.ArmorBreakingDamage;
|
||||||
|
}
|
||||||
|
if (damageInfo.elementFactors != null && damageInfo.elementFactors.Count > 0)
|
||||||
|
{
|
||||||
|
currentDamageFeatures |= DamageFeature.ElementalDamage;
|
||||||
|
}
|
||||||
|
if (damageInfo.buffChance > 0 || damageInfo.buff != null)
|
||||||
|
{
|
||||||
|
currentDamageFeatures |= DamageFeature.OnHitBuffApply;
|
||||||
|
}
|
||||||
|
if (damageInfo.bleedChance > 0)
|
||||||
|
{
|
||||||
|
currentDamageFeatures |= DamageFeature.OnHitBleed;
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach (var configuredFeature in audioDamageFeatures)
|
||||||
|
{
|
||||||
|
if (configuredFeature == DamageFeature.Undefined)
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if ((currentDamageFeatures & configuredFeature) == configuredFeature)
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
69
HitFeedback/DamageFeature.cs
Normal file
69
HitFeedback/DamageFeature.cs
Normal file
@@ -0,0 +1,69 @@
|
|||||||
|
using System;
|
||||||
|
|
||||||
|
namespace HitFeedback
|
||||||
|
{
|
||||||
|
[Flags]
|
||||||
|
public enum DamageFeature
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// 未指定或未分类的伤害特性。
|
||||||
|
/// </summary>
|
||||||
|
Undefined = 0,
|
||||||
|
/// <summary>
|
||||||
|
/// 普通物理伤害或其他未特别定义的伤害(对应 DamageTypes.normal)。
|
||||||
|
/// </summary>
|
||||||
|
NormalDamage = 1,
|
||||||
|
/// <summary>
|
||||||
|
/// 真实伤害,不受护甲或其他减伤效果影响(对应 DamageTypes.realDamage)。
|
||||||
|
/// </summary>
|
||||||
|
RealDamage = 2,
|
||||||
|
/// <summary>
|
||||||
|
/// 伤害来自增益(Buff)或持续效果。
|
||||||
|
/// (基于 DamageInfo.isFromBuffOrEffect)
|
||||||
|
/// </summary>
|
||||||
|
BuffOrEffectDamage = 4,
|
||||||
|
/// <summary>
|
||||||
|
/// 伤害无视目标的护甲。
|
||||||
|
/// (基于 DamageInfo.ignoreArmor)
|
||||||
|
/// </summary>
|
||||||
|
ArmorIgnoringDamage = 8,
|
||||||
|
/// <summary>
|
||||||
|
/// 伤害是暴击伤害。
|
||||||
|
/// (基于 DamageInfo.crit > 0)
|
||||||
|
/// </summary>
|
||||||
|
CriticalDamage = 16,
|
||||||
|
/// <summary>
|
||||||
|
/// 伤害包含护甲穿透效果。
|
||||||
|
/// (基于 DamageInfo.armorPiercing > 0)
|
||||||
|
/// </summary>
|
||||||
|
ArmorPiercingDamage = 32,
|
||||||
|
/// <summary>
|
||||||
|
/// 伤害是爆炸类型。
|
||||||
|
/// (基于 DamageInfo.isExplosion)
|
||||||
|
/// </summary>
|
||||||
|
ExplosionDamage = 64,
|
||||||
|
/// <summary>
|
||||||
|
/// 伤害具有护甲破坏效果。
|
||||||
|
/// (基于 DamageInfo.armorBreak > 0)
|
||||||
|
/// </summary>
|
||||||
|
ArmorBreakingDamage = 128,
|
||||||
|
/// <summary>
|
||||||
|
/// 伤害可能带有元素效果(如果有 elementFactors 存在且非空)。
|
||||||
|
/// </summary>
|
||||||
|
ElementalDamage = 256,
|
||||||
|
/// <summary>
|
||||||
|
/// 伤害可能附带Buff效果。
|
||||||
|
/// (基于 DamageInfo.buffChance > 0 或 buff != null)
|
||||||
|
/// </summary>
|
||||||
|
/// <remarks>
|
||||||
|
/// 注意:这个可以与 BuffOrEffectDamage 区分,BuffOrEffectDamage 是伤害本身来自Buff,
|
||||||
|
/// 而นี้是伤害造成时附带施加Buff的效果。
|
||||||
|
/// </remarks>
|
||||||
|
OnHitBuffApply = 512,
|
||||||
|
/// <summary>
|
||||||
|
/// 伤害可能附带流血效果。
|
||||||
|
/// (基于 DamageInfo.bleedChance > 0)
|
||||||
|
/// </summary>
|
||||||
|
OnHitBleed = 1024,
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -3,6 +3,7 @@ using System.Collections;
|
|||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using Duckov;
|
using Duckov;
|
||||||
|
using HitFeedback.Api;
|
||||||
using UnityEngine;
|
using UnityEngine;
|
||||||
using UnityEngine.Networking;
|
using UnityEngine.Networking;
|
||||||
using UnityEngine.SceneManagement;
|
using UnityEngine.SceneManagement;
|
||||||
@@ -11,24 +12,26 @@ using Random = UnityEngine.Random;
|
|||||||
|
|
||||||
namespace HitFeedback
|
namespace HitFeedback
|
||||||
{
|
{
|
||||||
public class ModBehaviour:Duckov.Modding.ModBehaviour
|
public class ModBehaviour : Duckov.Modding.ModBehaviour
|
||||||
{
|
{
|
||||||
public const string AudioFolderName = "audio";
|
public const string AudioFolderName = "audio";
|
||||||
public const string ConfigFileName = "config.ini";
|
public const string ConfigFileName = "config.ini";
|
||||||
public string audioFolderPath;
|
public string audioFolderPath;
|
||||||
public string configFilePath;
|
public string configFilePath;
|
||||||
|
|
||||||
public Dictionary<string,float> audioFilePath = new Dictionary<string,float>();
|
public Dictionary<string, float> audioProbability = new Dictionary<string, float>();
|
||||||
|
|
||||||
public Health health;
|
public Health health;
|
||||||
|
|
||||||
public Config config=new Config();
|
public Config config = new Config();
|
||||||
|
|
||||||
public float totalWeight;
|
public float totalWeight;
|
||||||
|
|
||||||
|
public const string MOD_SETTING_NAME = "受击反馈";
|
||||||
|
|
||||||
private void Update()
|
private void Update()
|
||||||
{
|
{
|
||||||
if (Input.GetKeyDown(KeyCode.F8))
|
if (Input.GetKeyDown(config.hotKey))
|
||||||
{
|
{
|
||||||
PlayRandomAudioClip();
|
PlayRandomAudioClip();
|
||||||
}
|
}
|
||||||
@@ -37,16 +40,16 @@ namespace HitFeedback
|
|||||||
protected override void OnAfterSetup()
|
protected override void OnAfterSetup()
|
||||||
{
|
{
|
||||||
LevelManager.OnLevelInitialized += OnSceneLoaded;
|
LevelManager.OnLevelInitialized += OnSceneLoaded;
|
||||||
audioFolderPath=Path.Combine(info.path,AudioFolderName);
|
audioFolderPath = Path.Combine(info.path, AudioFolderName);
|
||||||
configFilePath=Path.Combine(info.path, ConfigFileName);
|
configFilePath = Path.Combine(info.path, ConfigFileName);
|
||||||
FindWavFiles();
|
FindWavFiles();
|
||||||
config.LoadConfig(configFilePath);
|
config.LoadConfig(configFilePath);
|
||||||
ApplyConfig();
|
ApplyConfig();
|
||||||
foreach (var f in audioFilePath)
|
|
||||||
{
|
InitializeSetting();
|
||||||
totalWeight+=f.Value;
|
UpdateTotalWeight();
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override void OnBeforeDeactivate()
|
protected override void OnBeforeDeactivate()
|
||||||
{
|
{
|
||||||
LevelManager.OnLevelInitialized -= OnSceneLoaded;
|
LevelManager.OnLevelInitialized -= OnSceneLoaded;
|
||||||
@@ -58,13 +61,22 @@ namespace HitFeedback
|
|||||||
SaveConfig();
|
SaveConfig();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void UpdateTotalWeight()
|
||||||
|
{
|
||||||
|
totalWeight = 0;
|
||||||
|
foreach (var f in audioProbability)
|
||||||
|
{
|
||||||
|
totalWeight += f.Value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private void ApplyConfig()
|
private void ApplyConfig()
|
||||||
{
|
{
|
||||||
foreach (var f in config.probability)
|
foreach (var f in config.probability)
|
||||||
{
|
{
|
||||||
if (audioFilePath.ContainsKey(f.Key))
|
if (audioProbability.ContainsKey(f.Key))
|
||||||
{
|
{
|
||||||
audioFilePath[f.Key] = f.Value;
|
audioProbability[f.Key] = f.Value;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -72,28 +84,36 @@ namespace HitFeedback
|
|||||||
private void SaveConfig()
|
private void SaveConfig()
|
||||||
{
|
{
|
||||||
config.probability.Clear();
|
config.probability.Clear();
|
||||||
foreach (var f in audioFilePath)
|
foreach (var f in audioProbability)
|
||||||
{
|
{
|
||||||
config.probability.Add(f.Key, f.Value);
|
config.probability.Add(f.Key, f.Value);
|
||||||
}
|
}
|
||||||
|
|
||||||
config.SaveConfig(configFilePath);
|
config.SaveConfig(configFilePath);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void FindWavFiles()
|
private void FindWavFiles()
|
||||||
{
|
{
|
||||||
audioFilePath.Clear();
|
audioProbability.Clear();
|
||||||
if (!Directory.Exists(audioFolderPath))
|
if (!Directory.Exists(audioFolderPath))
|
||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
string[] files = Directory.GetFiles(audioFolderPath, "*.wav", SearchOption.TopDirectoryOnly);
|
var audioFiles = new List<string>();
|
||||||
if (files.Length > 0)
|
string[] wavFiles = Directory.GetFiles(audioFolderPath, "*.wav", SearchOption.TopDirectoryOnly);
|
||||||
|
audioFiles.AddRange(wavFiles);
|
||||||
|
string[] mp3Files = Directory.GetFiles(audioFolderPath, "*.mp3", SearchOption.TopDirectoryOnly);
|
||||||
|
audioFiles.AddRange(mp3Files);
|
||||||
|
string[] oggFiles = Directory.GetFiles(audioFolderPath, "*.ogg", SearchOption.TopDirectoryOnly);
|
||||||
|
audioFiles.AddRange(oggFiles);
|
||||||
|
if (audioFiles.Count > 0)
|
||||||
{
|
{
|
||||||
foreach (string filePath in files)
|
foreach (var filePath in audioFiles)
|
||||||
{
|
{
|
||||||
audioFilePath.Add(Path.GetFileName(filePath), 1);
|
audioProbability.Add(Path.GetFileName(filePath), 1);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -120,8 +140,10 @@ namespace HitFeedback
|
|||||||
{
|
{
|
||||||
if (health)
|
if (health)
|
||||||
{
|
{
|
||||||
|
|
||||||
health.OnHurtEvent.RemoveListener(OnHurtEvent);
|
health.OnHurtEvent.RemoveListener(OnHurtEvent);
|
||||||
}
|
}
|
||||||
|
|
||||||
health = CharacterMainControl.Main?.Health;
|
health = CharacterMainControl.Main?.Health;
|
||||||
if (health)
|
if (health)
|
||||||
{
|
{
|
||||||
@@ -132,15 +154,16 @@ namespace HitFeedback
|
|||||||
|
|
||||||
private void OnHurtEvent(DamageInfo damageInfo)
|
private void OnHurtEvent(DamageInfo damageInfo)
|
||||||
{
|
{
|
||||||
PlayRandomAudioClip();
|
if (config.ShouldPlayAudioFeedback(damageInfo))
|
||||||
|
PlayRandomAudioClip();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void PlayRandomAudioClip()
|
public void PlayRandomAudioClip()
|
||||||
{
|
{
|
||||||
if (audioFilePath.Count > 0)
|
if (audioProbability.Count > 0)
|
||||||
{
|
{
|
||||||
var randomIndex = Random.Range(0, totalWeight);
|
var randomIndex = Random.Range(0, totalWeight);
|
||||||
foreach (var f in audioFilePath)
|
foreach (var f in audioProbability)
|
||||||
{
|
{
|
||||||
randomIndex -= f.Value;
|
randomIndex -= f.Value;
|
||||||
if (randomIndex <= 0)
|
if (randomIndex <= 0)
|
||||||
@@ -155,5 +178,182 @@ namespace HitFeedback
|
|||||||
Debug.LogWarning("Mod Feedback: No audio clips loaded to play.");
|
Debug.LogWarning("Mod Feedback: No audio clips loaded to play.");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void InitializeSetting()
|
||||||
|
{
|
||||||
|
if (!Api.ModConfigAPI.Initialize())
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
foreach (var audio in audioProbability)
|
||||||
|
{
|
||||||
|
ModConfigAPI.SafeAddInputWithSlider(MOD_SETTING_NAME, audio.Key, $"音频\"{audio.Key}\"播放概率",
|
||||||
|
typeof(float), audio.Value, new Vector2(0, 100));
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach (DamageFeature value in Enum.GetValues(typeof(DamageFeature)))
|
||||||
|
{
|
||||||
|
ModConfigAPI.SafeAddBoolDropdownList(MOD_SETTING_NAME, value.ToString(),
|
||||||
|
$"受到{ToSingleFeatureChineseString(value)}时触发",
|
||||||
|
config.audioDamageFeatures.Contains(value));
|
||||||
|
}
|
||||||
|
|
||||||
|
var hotkeyOptions = GenerateCommonKeyCodeOptions();
|
||||||
|
ModConfigAPI.SafeAddDropdownList(
|
||||||
|
MOD_SETTING_NAME,
|
||||||
|
"hotKey",
|
||||||
|
"主动触发的热键",
|
||||||
|
hotkeyOptions,
|
||||||
|
typeof(int),
|
||||||
|
config.hotKey
|
||||||
|
);
|
||||||
|
|
||||||
|
|
||||||
|
ModConfigAPI.SafeAddOnOptionsChangedDelegate(OnConfigChange);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
private void OnConfigChange(string key)
|
||||||
|
{
|
||||||
|
key = key[(MOD_SETTING_NAME.Length + 1)..];
|
||||||
|
if (key == "hotKey")
|
||||||
|
{
|
||||||
|
config.hotKey = (KeyCode)ModConfigAPI.SafeLoad(MOD_SETTING_NAME, key, (int)(config.hotKey));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (audioProbability.ContainsKey(key))
|
||||||
|
{
|
||||||
|
var value = ModConfigAPI.SafeLoad(MOD_SETTING_NAME, key, audioProbability[key]);
|
||||||
|
audioProbability[key] = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (Enum.TryParse(key, out DamageFeature damageInfo))
|
||||||
|
{
|
||||||
|
var current=config.audioDamageFeatures.Contains(damageInfo);
|
||||||
|
if (ModConfigAPI.SafeLoad(MOD_SETTING_NAME, key, current))
|
||||||
|
{
|
||||||
|
config.audioDamageFeatures.Add(damageInfo);
|
||||||
|
}
|
||||||
|
else if (current)
|
||||||
|
{
|
||||||
|
config.audioDamageFeatures.Remove(damageInfo);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
UpdateTotalWeight();
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 生成包含常用 KeyCode 的 SortedDictionary。
|
||||||
|
/// </summary>
|
||||||
|
/// <returns>一个 SortedDictionary,键是 KeyCode 的字符串表示,值是 KeyCode 枚举本身。</returns>
|
||||||
|
private static SortedDictionary<string, object> GenerateCommonKeyCodeOptions()
|
||||||
|
{
|
||||||
|
var options = new SortedDictionary<string, object>();
|
||||||
|
// 字母键
|
||||||
|
for (var c = 'A'; c <= 'Z'; c++)
|
||||||
|
{
|
||||||
|
var keyCode = (int)(KeyCode)Enum.Parse(typeof(KeyCode), c.ToString());
|
||||||
|
options.Add(c.ToString(), keyCode);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 数字键(主键盘)
|
||||||
|
for (var i = 0; i <= 9; i++)
|
||||||
|
{
|
||||||
|
var keyCode = (int)(KeyCode)Enum.Parse(typeof(KeyCode), "Alpha" + i.ToString());
|
||||||
|
options.Add(i.ToString(), keyCode);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 数字键盘
|
||||||
|
for (var i = 0; i <= 9; i++)
|
||||||
|
{
|
||||||
|
var keyCode = (int)(KeyCode)Enum.Parse(typeof(KeyCode), "Keypad" + i.ToString());
|
||||||
|
options.Add($"Num_{i}", keyCode); // 加前缀区分主键盘数字
|
||||||
|
}
|
||||||
|
|
||||||
|
// 功能键
|
||||||
|
for (var i = 1; i <= 12; i++)
|
||||||
|
{
|
||||||
|
var keyCode = (int)(KeyCode)Enum.Parse(typeof(KeyCode), "F" + i.ToString());
|
||||||
|
options.Add($"F{i}", keyCode);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 常用控制键
|
||||||
|
options.Add("空格", (int)KeyCode.Space);
|
||||||
|
options.Add("回车", (int)KeyCode.Return);
|
||||||
|
options.Add("Esc", (int)KeyCode.Escape);
|
||||||
|
options.Add("Shift (左)", (int)KeyCode.LeftShift);
|
||||||
|
options.Add("Shift (右)", (int)KeyCode.RightShift);
|
||||||
|
options.Add("Ctrl (左)", (int)KeyCode.LeftControl);
|
||||||
|
options.Add("Ctrl (右)", (int)KeyCode.RightControl);
|
||||||
|
options.Add("Alt (左)", (int)KeyCode.LeftAlt);
|
||||||
|
options.Add("Alt (右)", (int)KeyCode.RightAlt);
|
||||||
|
options.Add("Tab", (int)KeyCode.Tab);
|
||||||
|
options.Add("Backspace", (int)KeyCode.Backspace);
|
||||||
|
options.Add("Delete", (int)KeyCode.Delete);
|
||||||
|
options.Add("Home", (int)KeyCode.Home);
|
||||||
|
options.Add("End", (int)KeyCode.End);
|
||||||
|
options.Add("PageUp", (int)KeyCode.PageUp);
|
||||||
|
options.Add("PageDown", (int)KeyCode.PageDown);
|
||||||
|
options.Add("插入", (int)KeyCode.Insert);
|
||||||
|
// 方向键
|
||||||
|
options.Add("向上", (int)KeyCode.UpArrow);
|
||||||
|
options.Add("向下", (int)KeyCode.DownArrow);
|
||||||
|
options.Add("向左", (int)KeyCode.LeftArrow);
|
||||||
|
options.Add("向右", (int)KeyCode.RightArrow);
|
||||||
|
// 鼠标按键
|
||||||
|
options.Add("鼠标左键", (int)KeyCode.Mouse0);
|
||||||
|
options.Add("鼠标右键", (int)KeyCode.Mouse1);
|
||||||
|
options.Add("鼠标中键", (int)KeyCode.Mouse2);
|
||||||
|
// 其他一些常用键
|
||||||
|
options.Add("~", (int)KeyCode.BackQuote);
|
||||||
|
options.Add("-", (int)KeyCode.Minus);
|
||||||
|
options.Add("=", (int)KeyCode.Equals);
|
||||||
|
options.Add("[", (int)KeyCode.LeftBracket);
|
||||||
|
options.Add("]", (int)KeyCode.RightBracket);
|
||||||
|
options.Add("\\", (int)KeyCode.Backslash);
|
||||||
|
options.Add(";", (int)KeyCode.Semicolon);
|
||||||
|
options.Add("'", (int)KeyCode.Quote);
|
||||||
|
options.Add(",", (int)KeyCode.Comma);
|
||||||
|
options.Add(".", (int)KeyCode.Period);
|
||||||
|
options.Add("/", (int)KeyCode.Slash);
|
||||||
|
return options;
|
||||||
|
}
|
||||||
|
private static string ToSingleFeatureChineseString(DamageFeature feature)
|
||||||
|
{
|
||||||
|
switch (feature)
|
||||||
|
{
|
||||||
|
case DamageFeature.Undefined:
|
||||||
|
return "未指定特性";
|
||||||
|
case DamageFeature.NormalDamage:
|
||||||
|
return "普通伤害";
|
||||||
|
case DamageFeature.RealDamage:
|
||||||
|
return "真实伤害";
|
||||||
|
case DamageFeature.BuffOrEffectDamage:
|
||||||
|
return "增益/效果伤害";
|
||||||
|
case DamageFeature.ArmorIgnoringDamage:
|
||||||
|
return "无视护甲伤害";
|
||||||
|
case DamageFeature.CriticalDamage:
|
||||||
|
return "暴击伤害";
|
||||||
|
case DamageFeature.ArmorPiercingDamage:
|
||||||
|
return "护甲穿透伤害";
|
||||||
|
case DamageFeature.ExplosionDamage:
|
||||||
|
return "爆炸伤害";
|
||||||
|
case DamageFeature.ArmorBreakingDamage:
|
||||||
|
return "护甲破坏伤害";
|
||||||
|
case DamageFeature.ElementalDamage:
|
||||||
|
return "元素伤害";
|
||||||
|
case DamageFeature.OnHitBuffApply:
|
||||||
|
return "命中附加增益";
|
||||||
|
case DamageFeature.OnHitBleed:
|
||||||
|
return "命中附加流血";
|
||||||
|
default:
|
||||||
|
return "未知特性"; // 处理未定义或将来添加的特性
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -13,7 +13,7 @@ using System.Reflection;
|
|||||||
[assembly: System.Reflection.AssemblyCompanyAttribute("HitFeedback")]
|
[assembly: System.Reflection.AssemblyCompanyAttribute("HitFeedback")]
|
||||||
[assembly: System.Reflection.AssemblyConfigurationAttribute("Release")]
|
[assembly: System.Reflection.AssemblyConfigurationAttribute("Release")]
|
||||||
[assembly: System.Reflection.AssemblyFileVersionAttribute("1.0.0.0")]
|
[assembly: System.Reflection.AssemblyFileVersionAttribute("1.0.0.0")]
|
||||||
[assembly: System.Reflection.AssemblyInformationalVersionAttribute("1.0.0+0206a83f56b5a794fe2f173b4a047cc4f0d4cd90")]
|
[assembly: System.Reflection.AssemblyInformationalVersionAttribute("1.0.0+5d69efbc3f80a5422cef0884e02fb27adf20b467")]
|
||||||
[assembly: System.Reflection.AssemblyProductAttribute("HitFeedback")]
|
[assembly: System.Reflection.AssemblyProductAttribute("HitFeedback")]
|
||||||
[assembly: System.Reflection.AssemblyTitleAttribute("HitFeedback")]
|
[assembly: System.Reflection.AssemblyTitleAttribute("HitFeedback")]
|
||||||
[assembly: System.Reflection.AssemblyVersionAttribute("1.0.0.0")]
|
[assembly: System.Reflection.AssemblyVersionAttribute("1.0.0.0")]
|
||||||
|
|||||||
@@ -1 +1 @@
|
|||||||
044e9b3ac36cbf242c64f55f40a9077d40727cb97e540d335e1e29dbc7dccff1
|
fa83406df4fd7108a6156ab0271b4cddce6abc73c66cbcefdb2f1419643481ef
|
||||||
|
|||||||
Binary file not shown.
@@ -1 +1 @@
|
|||||||
17059da2b4ba38151f18bb4edeeed66662c124b29efddf4c48967bc01f547046
|
86c7d211b447f2631e25f9656fa8ef4b9b63f5a671cedbbe00854ccaccdd00b3
|
||||||
|
|||||||
Binary file not shown.
@@ -1 +1 @@
|
|||||||
17620100186005303
|
17623343068138064
|
||||||
@@ -1,490 +1,465 @@
|
|||||||
using System;
|
|
||||||
using System.Collections;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.IO;
|
|
||||||
using System.Reflection;
|
|
||||||
using System.Text;
|
|
||||||
using UnityEngine;
|
using UnityEngine;
|
||||||
using UnityEngine.EventSystems;
|
|
||||||
using UnityEngine.SceneManagement;
|
using UnityEngine.SceneManagement;
|
||||||
|
using UnityEngine.EventSystems; // 用于UI射线检测
|
||||||
|
using System;
|
||||||
|
using System.IO;
|
||||||
|
using System.Text;
|
||||||
|
using System.Reflection;
|
||||||
|
using System.Collections; // 用于 IEnumerable
|
||||||
|
using System.Collections.Generic; // 用于 HashSet, List, Dictionary
|
||||||
|
using System.Linq; // 用于 OrderBy
|
||||||
|
|
||||||
namespace SceneSnapshot
|
namespace SceneSnapshot
|
||||||
{
|
{
|
||||||
internal class PrintTool : MonoBehaviour
|
internal class PrintTool : MonoBehaviour
|
||||||
{
|
{
|
||||||
private const string FOLDER_NAME = "GameObjectSnapshots";
|
private const string BASE_FOLDER_NAME = "GameObjectSnapshots"; // 主文件夹名称
|
||||||
private const int MAX_REFLECTION_DEPTH = 3; // 最大反射深度,防止循环引用或过深的对象图
|
private const int MAX_REFLECTION_DEPTH = 3; // 最大反射深度,防止循环引用或过深的对象图
|
||||||
private const int MAX_COLLECTION_ELEMENTS_TO_PRINT = 5; // 集合最多打印的元素数量
|
private const int MAX_COLLECTION_ELEMENTS_TO_PRINT = 5; // 集合最多打印的元素数量
|
||||||
|
|
||||||
private void Update()
|
private void Update()
|
||||||
{
|
{
|
||||||
if (Input.GetKeyDown(KeyCode.F2)) CaptureAndPrintSceneInfo();
|
if (Input.GetKeyDown(KeyCode.F2))
|
||||||
|
{
|
||||||
|
CaptureAndPrintSnapshot();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void CaptureAndPrintSceneInfo()
|
private void CaptureAndPrintSnapshot()
|
||||||
{
|
{
|
||||||
var desktopPath = Environment.GetFolderPath(Environment.SpecialFolder.Desktop);
|
// 获取桌面路径
|
||||||
var outputFolderPath = Path.Combine(desktopPath, FOLDER_NAME);
|
string desktopPath = Environment.GetFolderPath(Environment.SpecialFolder.Desktop);
|
||||||
|
|
||||||
|
// 1. 创建主文件夹 (如果不存在)
|
||||||
|
string baseOutputPath = Path.Combine(desktopPath, BASE_FOLDER_NAME);
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
if (!Directory.Exists(outputFolderPath))
|
if (!Directory.Exists(baseOutputPath))
|
||||||
{
|
{
|
||||||
Directory.CreateDirectory(outputFolderPath);
|
Directory.CreateDirectory(baseOutputPath);
|
||||||
Debug.Log($"创建输出文件夹: {outputFolderPath}");
|
Debug.Log($"已创建主快照文件夹: {baseOutputPath}");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception e)
|
||||||
{
|
{
|
||||||
Debug.LogError($"创建文件夹失败: {outputFolderPath} - {ex.Message}");
|
Debug.LogError($"无法创建主快照文件夹 {baseOutputPath}: {e.Message}");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
var activeSceneName = SceneManager.GetActiveScene().name;
|
// 2. 在主文件夹内创建带时间戳的子文件夹
|
||||||
var timestamp = DateTime.Now.ToString("yyyyMMdd_HHmmss");
|
string timestampFolderName = DateTime.Now.ToString("yyyyMMdd_HHmmss");
|
||||||
var fileName = $"{activeSceneName}_FullSnapshot_{timestamp}.txt"; // 修改文件名以示区别
|
string currentSnapshotOutputPath = Path.Combine(baseOutputPath, timestampFolderName);
|
||||||
var fullFilePath = Path.Combine(outputFolderPath, fileName);
|
|
||||||
|
|
||||||
var sb = new StringBuilder();
|
|
||||||
|
|
||||||
sb.AppendLine("=================================================");
|
|
||||||
sb.AppendLine($"场景信息快照 - 活跃场景: {activeSceneName}");
|
|
||||||
sb.AppendLine($"生成时间: {DateTime.Now}");
|
|
||||||
sb.AppendLine("=================================================");
|
|
||||||
sb.AppendLine();
|
|
||||||
|
|
||||||
sb.AppendLine("--- 鼠标位置对象信息 ---");
|
|
||||||
AppendMouseHoverObjectInfo(sb);
|
|
||||||
sb.AppendLine();
|
|
||||||
|
|
||||||
sb.AppendLine("--- 所有加载场景的活跃游戏对象层次结构及其组件 ---");
|
|
||||||
|
|
||||||
// 遍历所有已加载的场景
|
|
||||||
for (var i = 0; i < SceneManager.sceneCount; i++)
|
|
||||||
{
|
|
||||||
var currentScene = SceneManager.GetSceneAt(i);
|
|
||||||
|
|
||||||
// 打印场景名称作为分割线
|
|
||||||
sb.AppendLine($"\n===== 场景: {currentScene.name} ===== " +
|
|
||||||
(currentScene == SceneManager.GetActiveScene() ? "(活跃场景)" : ""));
|
|
||||||
|
|
||||||
GameObject[] rootObjects = currentScene.GetRootGameObjects();
|
|
||||||
if (rootObjects.Length == 0)
|
|
||||||
sb.AppendLine(" - 该场景没有根游戏对象。");
|
|
||||||
else
|
|
||||||
foreach (var go in rootObjects)
|
|
||||||
AppendGameObjectInfo(go, 0, sb);
|
|
||||||
}
|
|
||||||
|
|
||||||
sb.AppendLine("=================================================");
|
|
||||||
|
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
File.WriteAllText(fullFilePath, sb.ToString(), Encoding.UTF8);
|
if (!Directory.Exists(currentSnapshotOutputPath))
|
||||||
Debug.Log($"场景信息已成功保存到: {fullFilePath}");
|
{
|
||||||
|
Directory.CreateDirectory(currentSnapshotOutputPath);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception e)
|
||||||
{
|
{
|
||||||
Debug.LogError($"保存文件失败: {fullFilePath} - {ex.Message}");
|
Debug.LogError($"无法创建快照子文件夹 {currentSnapshotOutputPath}: {e.Message}");
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 递归地将游戏对象的名称、活跃状态、组件及其子对象的层次结构追加到StringBuilder。
|
|
||||||
/// **注意:此方法只会处理活跃状态为 activeSelf 的对象。**
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="go">要处理的游戏对象。</param>
|
|
||||||
/// <param name="indentLevel">当前缩进级别。</param>
|
|
||||||
/// <param name="sb">StringBuilder实例。</param>
|
|
||||||
private void AppendGameObjectInfo(GameObject go, int indentLevel, StringBuilder sb)
|
|
||||||
{
|
|
||||||
// 只有当对象自身是激活状态时才处理和打印
|
|
||||||
if (!go || !go.activeSelf) return;
|
|
||||||
|
|
||||||
var indent = new string(' ', indentLevel * 4); // 每个层级使用4个空格缩进
|
|
||||||
|
|
||||||
// 打印游戏对象名称和活跃状态
|
|
||||||
sb.AppendLine(
|
|
||||||
$"{indent}[{go.name}] (ActiveSelf: {go.activeSelf}, ActiveInHierarchy: {go.activeInHierarchy})");
|
|
||||||
|
|
||||||
// 打印所有组件
|
|
||||||
var components = go.GetComponents<Component>();
|
|
||||||
foreach (var comp in components)
|
|
||||||
if (comp) // 某些组件可能在运行时被销毁
|
|
||||||
sb.AppendLine($"{indent} - Component: {comp.GetType().Name}");
|
|
||||||
|
|
||||||
// 递归处理子对象
|
|
||||||
foreach (Transform child in go.transform) AppendGameObjectInfo(child.gameObject, indentLevel + 1, sb);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 尝试检测鼠标位置下方的UI元素或场景对象,并将其路径和组件信息追加到StringBuilder。
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="sb">StringBuilder实例。</param>
|
|
||||||
private void AppendMouseHoverObjectInfo(StringBuilder sb)
|
|
||||||
{
|
|
||||||
// 首先尝试Raycast UI元素
|
|
||||||
var eventDataCurrentPosition = new PointerEventData(EventSystem.current);
|
|
||||||
eventDataCurrentPosition.position = new Vector2(Input.mousePosition.x, Input.mousePosition.y);
|
|
||||||
var results = new List<RaycastResult>();
|
|
||||||
if (EventSystem.current)
|
|
||||||
{
|
|
||||||
EventSystem.current.RaycastAll(eventDataCurrentPosition, results);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (results.Count > 0)
|
|
||||||
{
|
|
||||||
// UI元素优先级更高
|
|
||||||
var uiObject = results[0].gameObject;
|
|
||||||
var uiPath = GetGameObjectPath(uiObject);
|
|
||||||
sb.AppendLine($"鼠标下方UI路径: {uiPath}");
|
|
||||||
sb.AppendLine($" - 所在场景: {uiObject.scene.name}");
|
|
||||||
|
|
||||||
// 添加UI对象组件信息
|
|
||||||
sb.AppendLine(" - UI对象组件信息:");
|
|
||||||
AppendGameObjectComponentInfo(sb, uiObject, " "); // 增加缩进
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// 如果没有UI元素,尝试Raycast场景对象
|
Debug.Log($"开始生成场景快照到: {currentSnapshotOutputPath}");
|
||||||
if (Camera.main != null)
|
|
||||||
{
|
|
||||||
var ray = Camera.main.ScreenPointToRay(Input.mousePosition);
|
|
||||||
RaycastHit hit;
|
|
||||||
if (Physics.Raycast(ray, out hit))
|
|
||||||
{
|
|
||||||
var sceneObjectPath = GetGameObjectPath(hit.collider.gameObject);
|
|
||||||
sb.AppendLine($"鼠标下方场景对象路径: {sceneObjectPath}");
|
|
||||||
sb.AppendLine($" - 所在场景: {hit.collider.gameObject.scene.name}");
|
|
||||||
|
|
||||||
// 添加场景对象组件信息
|
// Part 1: 打印所有对象的对象树及其组件
|
||||||
sb.AppendLine(" - 场景对象组件信息:");
|
PrintAllGameObjectsTree(currentSnapshotOutputPath);
|
||||||
AppendGameObjectComponentInfo(sb, hit.collider.gameObject, " "); // 增加缩进
|
|
||||||
return;
|
// Part 2: 打印鼠标位置对象的组件值
|
||||||
|
PrintMouseHoveredObjectDetails(currentSnapshotOutputPath);
|
||||||
|
|
||||||
|
Debug.Log("场景快照生成完毕!");
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 打印所有场景对象的对象树(包括DontDestroyOnLoad)及其组件。
|
||||||
|
/// </summary>
|
||||||
|
private void PrintAllGameObjectsTree(string outputPath)
|
||||||
|
{
|
||||||
|
StringBuilder sb = new StringBuilder();
|
||||||
|
sb.AppendLine("--- 所有激活场景对象树 ---");
|
||||||
|
sb.AppendLine("--------------------------\n");
|
||||||
|
|
||||||
|
// 用于存储按场景分组的根对象
|
||||||
|
var sceneRootGameObjects = new Dictionary<Scene, List<GameObject>>();
|
||||||
|
// 用于存储 DontDestroyOnLoad 对象
|
||||||
|
var dontDestroyOnLoadRoots = new List<GameObject>();
|
||||||
|
|
||||||
|
// 1. 遍历所有加载的场景,获取其根对象
|
||||||
|
for (int i = 0; i < SceneManager.sceneCount; i++)
|
||||||
|
{
|
||||||
|
Scene scene = SceneManager.GetSceneAt(i);
|
||||||
|
sceneRootGameObjects[scene] = new List<GameObject>(scene.GetRootGameObjects());
|
||||||
|
}
|
||||||
|
|
||||||
|
// 2. 查找 DontDestroyOnLoad 对象
|
||||||
|
// DontDestroyOnLoad 对象不属于任何通过 SceneManager.GetSceneAt 获取的“普通”场景
|
||||||
|
// 它们通常在特殊的 "DontDestroyOnLoad" 场景中(在Unity编辑器中可见),但在运行时无法直接通过 SceneManager.GetSceneAt 访问。
|
||||||
|
// 因此,我们遍历所有活跃的GameObject,找出那些是根对象但又不属于任何已知场景的。
|
||||||
|
GameObject[] allActiveGameObjectsInHierarchy = FindObjectsOfType<GameObject>(); // 获取所有活跃的GameObject
|
||||||
|
|
||||||
|
foreach (GameObject go in allActiveGameObjectsInHierarchy)
|
||||||
|
{
|
||||||
|
if (go.transform.parent == null) // 这是一个根对象
|
||||||
|
{
|
||||||
|
bool foundInLoadedScene = false;
|
||||||
|
foreach (var kvp in sceneRootGameObjects)
|
||||||
|
{
|
||||||
|
if (kvp.Value.Contains(go))
|
||||||
|
{
|
||||||
|
foundInLoadedScene = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!foundInLoadedScene)
|
||||||
|
{
|
||||||
|
// 如果它不是任何已加载场景的根对象,那么它可能是DontDestroyOnLoad对象
|
||||||
|
dontDestroyOnLoadRoots.Add(go);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 3. 打印普通场景的对象树
|
||||||
|
foreach (var kvp in sceneRootGameObjects)
|
||||||
|
{
|
||||||
|
Scene currentScene = kvp.Key;
|
||||||
|
List<GameObject> roots = kvp.Value;
|
||||||
|
|
||||||
|
sb.AppendLine($"=== 场景: {currentScene.name} (路径: {currentScene.path}, 已加载: {currentScene.isLoaded}) ===\n");
|
||||||
|
|
||||||
|
// 按名称排序根对象以保持输出一致性
|
||||||
|
foreach (GameObject root in roots.OrderBy(g => g.name))
|
||||||
|
{
|
||||||
|
PrintGameObjectRecursive(root, 0, sb, new HashSet<GameObject>());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 4. 打印 DontDestroyOnLoad 对象的对象树
|
||||||
|
if (dontDestroyOnLoadRoots.Count > 0)
|
||||||
|
{
|
||||||
|
// 检查是否已经有一个伪的 "DontDestroyOnLoad" 场景被 Unity 在某些情境下自动添加
|
||||||
|
// 如果是,为了避免重复,且让输出更清晰,可以先尝试移除这些。
|
||||||
|
// 但是在 FindObjectsOfType 之后再分组,这种方式更健壮,不用管它是否有“场景”
|
||||||
|
|
||||||
|
sb.AppendLine("\n=== DontDestroyOnLoad 对象 ===\n");
|
||||||
|
foreach (GameObject root in dontDestroyOnLoadRoots.OrderBy(g => g.name))
|
||||||
|
{
|
||||||
|
PrintGameObjectRecursive(root, 0, sb, new HashSet<GameObject>());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
string filePath = Path.Combine(outputPath, "SceneObjectTree.txt");
|
||||||
|
File.WriteAllText(filePath, sb.ToString(), Encoding.UTF8); // 使用UTF8编码以支持更多字符
|
||||||
|
Debug.Log($"场景对象树已保存到: {filePath}");
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 递归打印GameObject及其子级和组件。
|
||||||
|
/// </summary>
|
||||||
|
private void PrintGameObjectRecursive(GameObject go, int depth, StringBuilder sb, HashSet<GameObject> visited)
|
||||||
|
{
|
||||||
|
// 防止循环引用或重复打印
|
||||||
|
if (visited.Contains(go))
|
||||||
|
{
|
||||||
|
sb.AppendLine($"{GetIndent(depth)}{go.name} (循环引用检测到!)");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
visited.Add(go);
|
||||||
|
|
||||||
|
string indent = GetIndent(depth);
|
||||||
|
sb.AppendLine($"{indent}GameObject: {go.name} (激活状态: {go.activeSelf}, 标签: {go.tag}, 层: {LayerMask.LayerToName(go.layer)})");
|
||||||
|
|
||||||
|
Component[] components = go.GetComponents<Component>();
|
||||||
|
foreach (Component comp in components)
|
||||||
|
{
|
||||||
|
if (comp == null) continue; // 避免NRE,尽管不常见
|
||||||
|
sb.AppendLine($"{indent} 组件: {comp.GetType().Name}");
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int i = 0; i < go.transform.childCount; i++)
|
||||||
|
{
|
||||||
|
PrintGameObjectRecursive(go.transform.GetChild(i).gameObject, depth + 1, sb, visited);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 重要:在递归完成后通常不需要从visited中移除GameObject,
|
||||||
|
// 因为一个GameObject在对象树中只会以唯一的路径出现一次
|
||||||
|
// (除非它以某种非常规方式被引用,但这不属于标准GameObject层级)。
|
||||||
|
// 对于值类型或简单引用,可以在PrintObjectProperties中在处理完后移除。
|
||||||
|
// 对于GameObject层级,一旦访问完其所有子节点,它在该“分支”的任务就完成了。
|
||||||
|
// 如果不同根节点下可能会有相同的GameObject引用(例如通过Inspector引用),
|
||||||
|
// 那visited集合的作用是防止在*当前递归路径*中再次遇到同一个GameObject,从而避免死循环。
|
||||||
|
// 对于整个场景树的打印,visited集合可以保持不变,因为我们不期望同一个GameObject作为不同根节点的子物体链中的一部分。
|
||||||
|
// visited.Remove(go); // 对于GameObject树结构,这通常是不必要的,因为每个GameObject在树中只有一个父级。
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 打印鼠标位置对象的组件值。
|
||||||
|
/// </summary>
|
||||||
|
private void PrintMouseHoveredObjectDetails(string outputPath)
|
||||||
|
{
|
||||||
|
StringBuilder sb = new StringBuilder();
|
||||||
|
sb.AppendLine("--- 鼠标悬停对象详细信息 ---");
|
||||||
|
sb.AppendLine("----------------------------\n");
|
||||||
|
|
||||||
|
GameObject hoveredObject = GetHoveredObject();
|
||||||
|
|
||||||
|
if (hoveredObject != null)
|
||||||
|
{
|
||||||
|
sb.AppendLine($"悬停的GameObject: {hoveredObject.name} (激活状态: {hoveredObject.activeSelf}, 标签: {hoveredObject.tag}, 层: {LayerMask.LayerToName(hoveredObject.layer)})");
|
||||||
|
sb.AppendLine($"组件及其值:\n");
|
||||||
|
|
||||||
|
Component[] components = hoveredObject.GetComponents<Component>();
|
||||||
|
foreach (Component comp in components)
|
||||||
|
{
|
||||||
|
if (comp == null) continue;
|
||||||
|
sb.AppendLine($" === 组件: {comp.GetType().Name} ===");
|
||||||
|
// 使用反射打印组件的字段和属性值
|
||||||
|
PrintObjectProperties(comp, 0, sb, new HashSet<object>(), " "); // 初始缩进4个空格
|
||||||
|
sb.AppendLine();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
sb.AppendLine("警告: 场景中没有主摄像机(Camera.main)或未被标记为 'MainCamera'。无法检测鼠标下的场景对象。");
|
sb.AppendLine("当前鼠标下方没有对象。");
|
||||||
}
|
}
|
||||||
|
|
||||||
sb.AppendLine("鼠标位置处没有检测到UI元素或场景对象。");
|
string filePath = Path.Combine(outputPath, "MouseHoverObjectDetails.txt");
|
||||||
|
File.WriteAllText(filePath, sb.ToString(), Encoding.UTF8); // 使用UTF8编码以支持更多字符
|
||||||
|
Debug.Log($"鼠标悬停对象详情已保存到: {filePath}");
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 为指定的GameObject追加其所有组件的信息。
|
/// 获取鼠标下方的GameObject(优先UI,其次3D场景对象)。
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="sb">StringBuilder实例。</param>
|
private GameObject GetHoveredObject()
|
||||||
/// <param name="obj">目标GameObject。</param>
|
|
||||||
/// <param name="indent">当前的缩进字符串。</param>
|
|
||||||
private void AppendGameObjectComponentInfo(StringBuilder sb, GameObject obj, string indent)
|
|
||||||
{
|
{
|
||||||
Component[] components = obj.GetComponents<Component>();
|
// 优先检测UI对象
|
||||||
if (components.Length == 0)
|
if (EventSystem.current != null)
|
||||||
{
|
{
|
||||||
sb.AppendLine($"{indent} - 无组件");
|
PointerEventData eventData = new PointerEventData(EventSystem.current);
|
||||||
|
eventData.position = Input.mousePosition;
|
||||||
|
List<RaycastResult> uiRaycastResults = new List<RaycastResult>();
|
||||||
|
EventSystem.current.RaycastAll(eventData, uiRaycastResults);
|
||||||
|
|
||||||
|
// 过滤掉非 interactable 的UI元素或者不包含 CanvasRenderer 的元素,可能更关注可见和可交互的UI
|
||||||
|
foreach (var result in uiRaycastResults)
|
||||||
|
{
|
||||||
|
if (result.gameObject != null && result.gameObject.GetComponent<CanvasRenderer>() != null)
|
||||||
|
{
|
||||||
|
return result.gameObject; // 返回第一个有效的UI元素
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Debug.LogWarning("场景中没有EventSystem,无法检测UI对象。请确保场景中存在一个EventSystem GameObject。");
|
||||||
|
}
|
||||||
|
|
||||||
|
// 如果没有UI对象,则检测3D场景对象
|
||||||
|
if (Camera.main != null)
|
||||||
|
{
|
||||||
|
Ray ray = Camera.main.ScreenPointToRay(Input.mousePosition);
|
||||||
|
// 仅检测默认层,或可配置的层
|
||||||
|
if (Physics.Raycast(ray, out RaycastHit hit))
|
||||||
|
{
|
||||||
|
return hit.collider.gameObject;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Debug.LogWarning("场景中没有主摄像机(Tagged 'MainCamera'),无法进行3D射线检测。请确保主摄像机正确标记。");
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 使用反射递归打印对象的字段和属性值。
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="obj">要打印的对象。</param>
|
||||||
|
/// <param name="currentDepth">当前反射深度。</param>
|
||||||
|
/// <param name="sb">StringBuilder用于构建输出。</param>
|
||||||
|
/// <param name="visitedObjects">用于检测循环引用的已访问对象集合。</param>
|
||||||
|
/// <param name="indent">当前缩进字符串。</param>
|
||||||
|
private void PrintObjectProperties(object obj, int currentDepth, StringBuilder sb, HashSet<object> visitedObjects, string indent)
|
||||||
|
{
|
||||||
|
if (obj == null)
|
||||||
|
{
|
||||||
|
sb.AppendLine("null");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
foreach (var component in components)
|
Type type = obj.GetType();
|
||||||
|
|
||||||
|
// 1. 处理基本类型、字符串、枚举
|
||||||
|
// 注意:string是引用类型,但行为上通常被视为值类型,其ToString是其自身
|
||||||
|
if (type.IsPrimitive || obj is string || type.IsEnum || obj is decimal || obj is DateTime)
|
||||||
{
|
{
|
||||||
if (component == null)
|
sb.AppendLine($"({type.Name}) {obj}"); // 显示类型名,更清晰
|
||||||
{
|
return;
|
||||||
sb.AppendLine($"{indent} - (空组件)");
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
// 尝试获取Behaviour的enabled状态
|
|
||||||
string enabledStatus = (component is Behaviour behaviour) ? behaviour.enabled.ToString() : "N/A";
|
|
||||||
sb.AppendLine($"{indent} - 组件: {component.GetType().Name} (Enabled: {enabledStatus})");
|
|
||||||
AppendComponentPropertiesAndFields(sb, component, indent + " "); // 更深一层缩进,显示组件的属性/字段
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 利用反射为指定的组件追加其公共属性和字段的信息。
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="sb">StringBuilder实例。</param>
|
|
||||||
/// <param name="component">目标组件。</param>
|
|
||||||
/// <param name="indent">当前的缩进字符串。</param>
|
|
||||||
private void AppendComponentPropertiesAndFields(StringBuilder sb, Component component, string indent)
|
|
||||||
{
|
|
||||||
var bindingFlags = BindingFlags.Public | BindingFlags.Instance;
|
|
||||||
var componentType = component.GetType();
|
|
||||||
// 收集所有公共实例属性和字段
|
|
||||||
var members = new List<MemberInfo>();
|
|
||||||
members.AddRange(componentType.GetProperties(bindingFlags));
|
|
||||||
members.AddRange(componentType.GetFields(bindingFlags));
|
|
||||||
bool hasPrintedAnything = false;
|
|
||||||
foreach (var member in members)
|
|
||||||
{
|
|
||||||
// 排除一些常见或特定组件上可能导致冗余或问题的属性/字段
|
|
||||||
if (IsMemberToExclude(member, component)) continue;
|
|
||||||
object value = null;
|
|
||||||
string memberName = member.Name;
|
|
||||||
try
|
|
||||||
{
|
|
||||||
if (member is PropertyInfo pi)
|
|
||||||
{
|
|
||||||
if (pi.CanRead) // 确保属性可读
|
|
||||||
{
|
|
||||||
value = pi.GetValue(component);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
// 忽略不可读的属性
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else if (member is FieldInfo fi)
|
|
||||||
{
|
|
||||||
value = fi.GetValue(component);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
catch (Exception ex)
|
|
||||||
{
|
|
||||||
// 捕获获取值时可能发生的异常
|
|
||||||
sb.AppendLine($"{indent}- {memberName}: <获取失败: {ex.GetType().Name}>");
|
|
||||||
hasPrintedAnything = true;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
// 格式化值进行显示,初始深度为0
|
|
||||||
string formattedValue = FormatValueForDisplay(value, 0);
|
|
||||||
sb.AppendLine($"{indent}- {memberName}: {formattedValue}");
|
|
||||||
hasPrintedAnything = true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!hasPrintedAnything)
|
// 2. 处理常见的Unity值类型(如Vector3, Quaternion, Color, Rect等)
|
||||||
|
// 这些类型通常有很好的ToString()方法,且不应过度深入反射其内部字段
|
||||||
|
if (obj is Vector2 || obj is Vector3 || obj is Vector4 || obj is Quaternion ||
|
||||||
|
obj is Color || obj is Color32 || obj is Rect || obj is Bounds ||
|
||||||
|
obj is AnimationCurve || obj is LayerMask || obj is Matrix4x4)
|
||||||
{
|
{
|
||||||
sb.AppendLine($"{indent}- (无公共属性或字段)");
|
sb.AppendLine($"({type.Name}) {obj}"); // 显示类型名,更清晰
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
// 3. 处理UnityEngine.Object类型(但不是Component或GameObject本身)
|
||||||
/// <summary>
|
// 例如 Material, Texture, ScriptableObject等,通常只打印其名称或ToString()就足够
|
||||||
/// 判断一个成员是否应该被排除在打印列表之外。
|
if (typeof(UnityEngine.Object).IsAssignableFrom(type) && !(obj is Component) && !(obj is GameObject))
|
||||||
/// 用于过滤掉冗余或可能导致深度递归的成员。
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="member">要检查的MemberInfo。</param>
|
|
||||||
/// <param name="component">该成员所属的组件实例。</param>
|
|
||||||
/// <returns>如果应排除则返回true。</returns>
|
|
||||||
private bool IsMemberToExclude(MemberInfo member, Component component)
|
|
||||||
{
|
|
||||||
// 排除从UnityEngine.Object继承的常见属性,这些通常是GameObject级别的元数据,
|
|
||||||
// 或者可能导致不必要的递归。
|
|
||||||
// 特别是gameObject和transform,它们的类型就是GameObject和Transform,递归它们没有意义,
|
|
||||||
// 且其值就是组件所依附的GameObject和Transform,已经通过GetGameObjectPath显示了。
|
|
||||||
switch (member.Name)
|
|
||||||
{
|
{
|
||||||
case "hideFlags": // Unity内部的标志,通常不需要显示
|
// 对于这些Unity对象,ToString()通常会返回对象名和类型,足够了
|
||||||
case "name": // GameObject的名称,已在路径中显示
|
sb.AppendLine($"({type.Name}) {obj.ToString()}");
|
||||||
case "tag": // GameObject的标签,可从GameObject直接获取
|
return;
|
||||||
case "layer": // GameObject的层,可从GameObject直接获取
|
|
||||||
case "useGUILayout": // Unity内部GUI相关的,通常不作为组件值关心
|
|
||||||
case "runInEditMode": // Unity编辑器模式相关,通常不作为组件值关心
|
|
||||||
// 对于Component基类上的gameObject和transform属性,它们直接指向宿主对象和其Transform。
|
|
||||||
// 打印它们本身就是重复的且可能误导(不是组件内部的独特“值”)。
|
|
||||||
case "gameObject":
|
|
||||||
case "transform":
|
|
||||||
case "isStatic": // GameObject的isStatic状态
|
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// 进一步过滤掉一些Unity内部或编辑器相关的属性,这些属性通常在运行时不提供有用的组件值信息。
|
// 4. 检查最大反射深度
|
||||||
if (member.DeclaringType == typeof(Behaviour) || member.DeclaringType == typeof(MonoBehaviour))
|
|
||||||
{
|
|
||||||
switch (member.Name)
|
|
||||||
{
|
|
||||||
case "isActiveAndEnabled": // 行为体的激活状态,通常与enabled一起考虑
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 格式化对象值以进行显示,特别是针对Unity的常见类型,以避免循环打印和提供简洁输出。
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="value">要格式化的值。</param>
|
|
||||||
/// <param name="currentDepth">当前的递归深度。</param>
|
|
||||||
/// <returns>格式化后的字符串。</returns>
|
|
||||||
private string FormatValueForDisplay(object value, int currentDepth = 0)
|
|
||||||
{
|
|
||||||
if (value == null) return "null";
|
|
||||||
Type type = value.GetType();
|
|
||||||
// 深度限制检查:如果超过最大深度,则返回提示信息
|
|
||||||
if (currentDepth >= MAX_REFLECTION_DEPTH)
|
if (currentDepth >= MAX_REFLECTION_DEPTH)
|
||||||
{
|
{
|
||||||
// 对于集合,提供更具体一些的信息
|
sb.AppendLine($"{indent}... (达到最大反射深度)");
|
||||||
if (value is Array array1) return $"Array (Count: {array1.Length}, Max Depth Reached)";
|
return;
|
||||||
if (value is IList list1) return $"List (Count: {list1.Count}, Max Depth Reached)";
|
|
||||||
if (value is IDictionary dictionary) return $"Dictionary (Count: {dictionary.Count}, Max Depth Reached)";
|
|
||||||
return $"[{type.Name} (Max Depth Reached)]";
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// 1. 基本类型、字符串、枚举:直接ToString()
|
// 5. 检查循环引用(仅对引用类型有效,且不是字符串那种特殊引用类型)
|
||||||
if (type.IsPrimitive || type == typeof(string) || type.IsEnum)
|
if (!type.IsValueType && !type.IsPrimitive && !(obj is string))
|
||||||
{
|
{
|
||||||
return value.ToString();
|
if (visitedObjects.Contains(obj))
|
||||||
|
{
|
||||||
|
sb.AppendLine($"{indent}... (检测到循环引用: {type.Name})");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
visitedObjects.Add(obj); // 标记为已访问
|
||||||
}
|
}
|
||||||
|
|
||||||
// 2. 常见的Unity结构体:特殊格式化,避免深度递归并提供简洁输出
|
// 6. 处理集合类型(数组、List、Dictionary等,但不包括字符串)
|
||||||
if (value is Vector2 vec2) return $"({vec2.x:F2}, {vec2.y:F2})";
|
if (obj is IEnumerable enumerable)
|
||||||
if (value is Vector3 vec3) return $"({vec3.x:F2}, {vec3.y:F2}, {vec3.z:F2})";
|
|
||||||
if (value is Vector4 vec4) return $"({vec4.x:F2}, {vec4.y:F2}, {vec4.z:F2}, {vec4.w:F2})";
|
|
||||||
|
|
||||||
// Quaternion默认ToString()会显示x,y,z,w,但有时EulerAngles更直观。
|
|
||||||
if (value is Quaternion q)
|
|
||||||
return
|
|
||||||
$"Q({q.x:F2}, {q.y:F2}, {q.z:F2}, {q.w:F2}) (Euler: {q.eulerAngles.x:F2}, {q.eulerAngles.y:F2}, {q.eulerAngles.z:F2})";
|
|
||||||
|
|
||||||
if (value is Color color) return $"RGBA({color.r:F2}, {color.g:F2}, {color.b:F2}, {color.a:F2})";
|
|
||||||
if (value is Rect rect)
|
|
||||||
return $"Rect(Pos:({rect.xMin:F2},{rect.yMin:F2}) Size:({rect.width:F2},{rect.height:F2}))";
|
|
||||||
if (value is Bounds bounds)
|
|
||||||
return
|
|
||||||
$"Bounds(Center:({bounds.center.x:F2},{bounds.center.y:F2},{bounds.center.z:F2}) Extents:({bounds.extents.x:F2},{bounds.extents.y:F2},{bounds.extents.z:F2}))";
|
|
||||||
if (value is LayerMask layerMask)
|
|
||||||
{
|
{
|
||||||
// LayerMask的值可能代表多个层,或一个单一层。LayerToName只能转换单一层。
|
// 对于字典,直接打印IEnumerable会导致键值对混乱,需要特殊处理
|
||||||
// 对于多个层,返回其原始值更有意义。
|
if (obj is IDictionary dictionary)
|
||||||
return $"LayerMask(Value: {layerMask.value})";
|
{
|
||||||
|
sb.AppendLine($"({type.Name}) Count={dictionary.Count} {{");
|
||||||
|
int count = 0;
|
||||||
|
foreach (DictionaryEntry entry in dictionary)
|
||||||
|
{
|
||||||
|
if (count >= MAX_COLLECTION_ELEMENTS_TO_PRINT)
|
||||||
|
{
|
||||||
|
sb.AppendLine($"{indent + " "}...(已截断,显示了{MAX_COLLECTION_ELEMENTS_TO_PRINT}对键值)");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
sb.Append($"{indent + " "}[Key]: ");
|
||||||
|
PrintObjectProperties(entry.Key, currentDepth + 1, sb, visitedObjects, indent + " "); // 额外的缩进
|
||||||
|
sb.Append($"{indent + " "}[Value]: ");
|
||||||
|
PrintObjectProperties(entry.Value, currentDepth + 1, sb, visitedObjects, indent + " "); // 额外的缩进
|
||||||
|
count++;
|
||||||
|
}
|
||||||
|
sb.AppendLine($"{indent}}}");
|
||||||
|
}
|
||||||
|
else // 普通的IEnumerable(List, Array等)
|
||||||
|
{
|
||||||
|
int elementCount = 0;
|
||||||
|
if (obj is ICollection collection)
|
||||||
|
elementCount = collection.Count;
|
||||||
|
else if (obj is Array array)
|
||||||
|
elementCount = array.Length;
|
||||||
|
else // 对于无法直接获取Count的IEnumerable,需要遍历统计
|
||||||
|
{
|
||||||
|
var list = new List<object>();
|
||||||
|
foreach (var item in enumerable) list.Add(item);
|
||||||
|
elementCount = list.Count;
|
||||||
|
enumerable = list; // 重新赋值为可以重复遍历的list
|
||||||
|
}
|
||||||
|
|
||||||
|
sb.AppendLine($"({type.Name}) Count={elementCount} [");
|
||||||
|
int count = 0;
|
||||||
|
foreach (var item in enumerable)
|
||||||
|
{
|
||||||
|
if (count >= MAX_COLLECTION_ELEMENTS_TO_PRINT)
|
||||||
|
{
|
||||||
|
sb.AppendLine($"{indent + " "}...(已截断,显示了{MAX_COLLECTION_ELEMENTS_TO_PRINT}个元素)");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
sb.Append($"{indent + " "}- ");
|
||||||
|
PrintObjectProperties(item, currentDepth + 1, sb, visitedObjects, indent + " ");
|
||||||
|
count++;
|
||||||
|
}
|
||||||
|
sb.AppendLine($"{indent}]");
|
||||||
|
}
|
||||||
|
|
||||||
|
// 集合本身通常不直接导致循环引用其自身,其内部元素才可能。
|
||||||
|
// 故在处理集合后可以从visitedObjects中移除集合对象,防止它阻止其他路径对它的访问。
|
||||||
|
if (!type.IsValueType && !type.IsPrimitive && !(obj is string)) visitedObjects.Remove(obj);
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// 3. GameObject/Component引用:只打印名称或类型,避免无限递归
|
// 7. 处理一般对象(类或结构体)的字段和属性
|
||||||
if (value is GameObject go) return $"GameObject:'{go.name}'";
|
sb.AppendLine($"({type.Name}) {{");
|
||||||
if (value is Component comp)
|
|
||||||
return $"Component:'{comp.GetType().Name}' on GameObject:'{comp.gameObject.name}'";
|
|
||||||
|
|
||||||
// 4. 集合类型:现在会打印内容,调用专门的辅助方法
|
BindingFlags flags = BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance;
|
||||||
if (value is Array array)
|
|
||||||
|
// 字段
|
||||||
|
FieldInfo[] fields = type.GetFields(flags);
|
||||||
|
foreach (FieldInfo field in fields)
|
||||||
{
|
{
|
||||||
return FormatCollectionForDisplay(array, currentDepth + 1);
|
if (field.IsStatic) continue; // 跳过静态字段
|
||||||
|
if (field.IsDefined(typeof(ObsoleteAttribute), true)) continue; // 跳过过时字段
|
||||||
|
|
||||||
|
string propertyIndent = indent + " ";
|
||||||
|
sb.Append($"{propertyIndent}{field.Name}: ");
|
||||||
|
try
|
||||||
|
{
|
||||||
|
object fieldValue = field.GetValue(obj);
|
||||||
|
PrintObjectProperties(fieldValue, currentDepth + 1, sb, visitedObjects, propertyIndent);
|
||||||
|
}
|
||||||
|
catch (Exception e)
|
||||||
|
{
|
||||||
|
sb.AppendLine($"<无法获取值: {e.Message}>");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (value is IList list)
|
// 属性
|
||||||
|
PropertyInfo[] properties = type.GetProperties(flags);
|
||||||
|
foreach (PropertyInfo prop in properties)
|
||||||
{
|
{
|
||||||
return FormatCollectionForDisplay(list, currentDepth + 1);
|
// 跳过特殊名称属性(如Unity内部的hideFlags)、不可读属性、带索引器属性、过时属性
|
||||||
|
if (prop.IsSpecialName || !prop.CanRead || prop.GetIndexParameters().Length > 0 ||
|
||||||
|
prop.IsDefined(typeof(ObsoleteAttribute), true)) continue;
|
||||||
|
|
||||||
|
string propertyIndent = indent + " ";
|
||||||
|
sb.Append($"{propertyIndent}{prop.Name}: ");
|
||||||
|
try
|
||||||
|
{
|
||||||
|
object propValue = prop.GetValue(obj);
|
||||||
|
PrintObjectProperties(propValue, currentDepth + 1, sb, visitedObjects, propertyIndent);
|
||||||
|
}
|
||||||
|
catch (Exception e)
|
||||||
|
{
|
||||||
|
sb.AppendLine($"<无法获取值: {e.Message}>");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (value is IDictionary dict)
|
sb.AppendLine($"{indent}}}");
|
||||||
{
|
|
||||||
return FormatCollectionForDisplay(dict, currentDepth + 1);
|
|
||||||
}
|
|
||||||
|
|
||||||
// 对于其他不可转换为Array/IList/IDictionary的IEnumerable,但又不是字符串的类型
|
// 在对象处理完毕后,从已访问集合中移除(如果它是引用类型),
|
||||||
if (value is IEnumerable enumerable && !(value is string))
|
// 这允许在对象图的不同路径中再次遇到它(如果需要),但防止当前路径的循环。
|
||||||
{
|
if (!type.IsValueType && !type.IsPrimitive && !(obj is string)) visitedObjects.Remove(obj);
|
||||||
return FormatCollectionForDisplay(enumerable, currentDepth + 1);
|
|
||||||
}
|
|
||||||
|
|
||||||
// 5. 其他复杂引用类型:只打印其类型名称,默认不深入其内部 (除非深度允许,但在深度限制前这里只会显示类型名)
|
|
||||||
return type.Name; // 例如: Material, Texture2D等,只显示类型名
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 格式化集合对象以进行显示,支持深度限制和元素数量限制。
|
/// 获取指定深度的缩进字符串。
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="collection">要格式化的集合。</param>
|
private string GetIndent(int depth)
|
||||||
/// <param name="currentDepth">当前的递归深度。</param>
|
|
||||||
/// <returns>格式化后的集合字符串。</returns>
|
|
||||||
private string FormatCollectionForDisplay(IEnumerable collection, int currentDepth)
|
|
||||||
{
|
{
|
||||||
if (collection == null) return "null collection";
|
return new string(' ', depth * 2);
|
||||||
Type collectionType = collection.GetType();
|
|
||||||
StringBuilder sb = new StringBuilder();
|
|
||||||
// 尝试获取集合的类型名,去除可能的反引号(用于泛型类型)
|
|
||||||
string collectionTypeName = collectionType.IsGenericType
|
|
||||||
? collectionType.Name.Substring(0, collectionType.Name.IndexOf('`'))
|
|
||||||
: collectionType.Name.Replace("[]", "");
|
|
||||||
sb.Append(collectionTypeName);
|
|
||||||
|
|
||||||
sb.Append(" [");
|
|
||||||
int i = 0;
|
|
||||||
foreach (var item in collection)
|
|
||||||
{
|
|
||||||
if (i >= MAX_COLLECTION_ELEMENTS_TO_PRINT)
|
|
||||||
{
|
|
||||||
sb.Append(", ...");
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (i > 0) sb.Append(", ");
|
|
||||||
if (item is DictionaryEntry entry) // 针对非泛型IDictionary
|
|
||||||
{
|
|
||||||
sb.Append(
|
|
||||||
$"{{{FormatValueForDisplay(entry.Key, currentDepth + 1)}: {FormatValueForDisplay(entry.Value, currentDepth + 1)}}}");
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
Type itemType = item?.GetType();
|
|
||||||
// 针对泛型IDictionary,其元素是KeyValuePair<TKey, TValue>
|
|
||||||
if (itemType != null && itemType.IsGenericType &&
|
|
||||||
itemType.GetGenericTypeDefinition() == typeof(KeyValuePair<,>))
|
|
||||||
{
|
|
||||||
// 使用反射获取Key和Value属性
|
|
||||||
PropertyInfo keyProperty = itemType.GetProperty("Key");
|
|
||||||
PropertyInfo valueProperty = itemType.GetProperty("Value");
|
|
||||||
if (keyProperty != null && valueProperty != null)
|
|
||||||
{
|
|
||||||
object key = keyProperty.GetValue(item);
|
|
||||||
object value = valueProperty.GetValue(item);
|
|
||||||
sb.Append(
|
|
||||||
$"{{{FormatValueForDisplay(key, currentDepth + 1)}: {FormatValueForDisplay(value, currentDepth + 1)}}}");
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
// Fallback in case Key/Value properties aren't found
|
|
||||||
sb.Append(FormatValueForDisplay(item, currentDepth + 1));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
// 格式化普通集合元素
|
|
||||||
sb.Append(FormatValueForDisplay(item, currentDepth + 1));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
i++;
|
|
||||||
}
|
|
||||||
|
|
||||||
// 尝试获取集合的实际数量
|
|
||||||
string countInfo = "N/A";
|
|
||||||
if (collection is ICollection c)
|
|
||||||
{
|
|
||||||
countInfo = c.Count.ToString();
|
|
||||||
}
|
|
||||||
else if (collection is Array a)
|
|
||||||
{
|
|
||||||
countInfo = a.Length.ToString();
|
|
||||||
}
|
|
||||||
|
|
||||||
sb.Append($"] (Count: {countInfo})");
|
|
||||||
return sb.ToString();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 获取给定游戏对象的完整层次路径。
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="go">要获取路径的游戏对象。</param>
|
|
||||||
/// <returns>游戏对象的完整路径,例如 "Parent/Child/Object"。</returns>
|
|
||||||
private string GetGameObjectPath(GameObject go)
|
|
||||||
{
|
|
||||||
if (go == null) return "N/A";
|
|
||||||
|
|
||||||
var path = go.name;
|
|
||||||
var currentTransform = go.transform;
|
|
||||||
|
|
||||||
while (currentTransform.parent != null)
|
|
||||||
{
|
|
||||||
currentTransform = currentTransform.parent;
|
|
||||||
path = currentTransform.name + "/" + path;
|
|
||||||
}
|
|
||||||
|
|
||||||
return path;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -13,7 +13,7 @@ using System.Reflection;
|
|||||||
[assembly: System.Reflection.AssemblyCompanyAttribute("折纸的小箱子")]
|
[assembly: System.Reflection.AssemblyCompanyAttribute("折纸的小箱子")]
|
||||||
[assembly: System.Reflection.AssemblyConfigurationAttribute("Release")]
|
[assembly: System.Reflection.AssemblyConfigurationAttribute("Release")]
|
||||||
[assembly: System.Reflection.AssemblyFileVersionAttribute("1.0.0")]
|
[assembly: System.Reflection.AssemblyFileVersionAttribute("1.0.0")]
|
||||||
[assembly: System.Reflection.AssemblyInformationalVersionAttribute("1.0.0+0206a83f56b5a794fe2f173b4a047cc4f0d4cd90")]
|
[assembly: System.Reflection.AssemblyInformationalVersionAttribute("1.0.0+5d69efbc3f80a5422cef0884e02fb27adf20b467")]
|
||||||
[assembly: System.Reflection.AssemblyProductAttribute("SceneSnapshot")]
|
[assembly: System.Reflection.AssemblyProductAttribute("SceneSnapshot")]
|
||||||
[assembly: System.Reflection.AssemblyTitleAttribute("SceneSnapshot")]
|
[assembly: System.Reflection.AssemblyTitleAttribute("SceneSnapshot")]
|
||||||
[assembly: System.Reflection.AssemblyVersionAttribute("1.0.0")]
|
[assembly: System.Reflection.AssemblyVersionAttribute("1.0.0")]
|
||||||
|
|||||||
@@ -1 +1 @@
|
|||||||
532b9b8318b6010ae03f608127d55a2c59d0b50d9243b633f698b5d460668837
|
4cb78221af3251625ca99f740da024b47f366123d3acdfd330074e35f359044e
|
||||||
|
|||||||
Binary file not shown.
@@ -1 +1 @@
|
|||||||
17619862901687226
|
17623343068138064
|
||||||
@@ -1,16 +1,18 @@
|
|||||||
using System;
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.IO;
|
||||||
|
using System.Text;
|
||||||
|
using UnityEngine;
|
||||||
|
|
||||||
namespace Theme
|
namespace Theme
|
||||||
{
|
{
|
||||||
public class ModBehaviour:Duckov.Modding.ModBehaviour
|
public class ModBehaviour : Duckov.Modding.ModBehaviour
|
||||||
{
|
{
|
||||||
|
private StringBuilder _outputStringBuilder;
|
||||||
protected override void OnAfterSetup()
|
protected override void OnAfterSetup()
|
||||||
{
|
{
|
||||||
base.OnAfterSetup();
|
|
||||||
}
|
|
||||||
protected override void OnBeforeDeactivate()
|
|
||||||
{
|
|
||||||
base.OnBeforeDeactivate();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -13,7 +13,7 @@ using System.Reflection;
|
|||||||
[assembly: System.Reflection.AssemblyCompanyAttribute("Theme")]
|
[assembly: System.Reflection.AssemblyCompanyAttribute("Theme")]
|
||||||
[assembly: System.Reflection.AssemblyConfigurationAttribute("Release")]
|
[assembly: System.Reflection.AssemblyConfigurationAttribute("Release")]
|
||||||
[assembly: System.Reflection.AssemblyFileVersionAttribute("1.0.0.0")]
|
[assembly: System.Reflection.AssemblyFileVersionAttribute("1.0.0.0")]
|
||||||
[assembly: System.Reflection.AssemblyInformationalVersionAttribute("1.0.0+0206a83f56b5a794fe2f173b4a047cc4f0d4cd90")]
|
[assembly: System.Reflection.AssemblyInformationalVersionAttribute("1.0.0+5d69efbc3f80a5422cef0884e02fb27adf20b467")]
|
||||||
[assembly: System.Reflection.AssemblyProductAttribute("Theme")]
|
[assembly: System.Reflection.AssemblyProductAttribute("Theme")]
|
||||||
[assembly: System.Reflection.AssemblyTitleAttribute("Theme")]
|
[assembly: System.Reflection.AssemblyTitleAttribute("Theme")]
|
||||||
[assembly: System.Reflection.AssemblyVersionAttribute("1.0.0.0")]
|
[assembly: System.Reflection.AssemblyVersionAttribute("1.0.0.0")]
|
||||||
|
|||||||
@@ -1 +1 @@
|
|||||||
9389535ab653bc716c7ef81ccb8542bee3753b2c1f4be9c952401b4ec0aa9c5d
|
c1519aee9b121f1df8699539b785671792d60ca0acb5c9d1d51d6039c2fa5de5
|
||||||
|
|||||||
1
Theme/obj/Release/Theme.csproj.CoreCompileInputs.cache
Normal file
1
Theme/obj/Release/Theme.csproj.CoreCompileInputs.cache
Normal file
@@ -0,0 +1 @@
|
|||||||
|
ff440d05ee1ce379e101695273a889a69b97880c4ff8ae9ffb67b8c73e02feef
|
||||||
7
Theme/obj/Release/Theme.csproj.FileListAbsolute.txt
Normal file
7
Theme/obj/Release/Theme.csproj.FileListAbsolute.txt
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
D:\steam\steamapps\common\Escape from Duckov\Duckov_Data\Mods\Theme\Theme.dll
|
||||||
|
D:\vs_project\DuckovMods\Theme\obj\Release\Theme.csproj.AssemblyReference.cache
|
||||||
|
D:\vs_project\DuckovMods\Theme\obj\Release\Theme.GeneratedMSBuildEditorConfig.editorconfig
|
||||||
|
D:\vs_project\DuckovMods\Theme\obj\Release\Theme.AssemblyInfoInputs.cache
|
||||||
|
D:\vs_project\DuckovMods\Theme\obj\Release\Theme.AssemblyInfo.cs
|
||||||
|
D:\vs_project\DuckovMods\Theme\obj\Release\Theme.csproj.CoreCompileInputs.cache
|
||||||
|
D:\vs_project\DuckovMods\Theme\obj\Release\Theme.dll
|
||||||
BIN
Theme/obj/Release/Theme.dll
Normal file
BIN
Theme/obj/Release/Theme.dll
Normal file
Binary file not shown.
1
Theme/obj/rider.project.model.nuget.info
Normal file
1
Theme/obj/rider.project.model.nuget.info
Normal file
@@ -0,0 +1 @@
|
|||||||
|
17622424783469959
|
||||||
@@ -1 +1 @@
|
|||||||
17622424783469959
|
17623343068138064
|
||||||
73
UIFrame/GameOriginMainMenuUI.cs
Normal file
73
UIFrame/GameOriginMainMenuUI.cs
Normal file
@@ -0,0 +1,73 @@
|
|||||||
|
using Duckov.UI;
|
||||||
|
using Duckov.Utilities;
|
||||||
|
using TMPro;
|
||||||
|
using UIFrame.Utilities;
|
||||||
|
using UnityEngine;
|
||||||
|
using UnityEngine.SceneManagement;
|
||||||
|
using UnityEngine.UI;
|
||||||
|
|
||||||
|
namespace UIFrame
|
||||||
|
{
|
||||||
|
public class GameOriginMainMenuUI
|
||||||
|
{
|
||||||
|
public GameObject mainMenuContainer;
|
||||||
|
public Image? title;
|
||||||
|
public TMP_Text[]? allTexts;
|
||||||
|
|
||||||
|
public Sprite titleSprite;
|
||||||
|
|
||||||
|
public bool linkMainMenu=false;
|
||||||
|
|
||||||
|
public void Initialize()
|
||||||
|
{
|
||||||
|
SceneLoader.onAfterSceneInitialize += OnAfterSceneInitialize;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Cleanup()
|
||||||
|
{
|
||||||
|
SceneLoader.onAfterSceneInitialize -= OnAfterSceneInitialize;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnAfterSceneInitialize(SceneLoadingContext sceneLoadingContext)
|
||||||
|
{
|
||||||
|
linkMainMenu = false;
|
||||||
|
LinkMainMenuObj();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void LinkMainMenuObj()
|
||||||
|
{
|
||||||
|
mainMenuContainer = GameObject.Find("MainMenuContainer");
|
||||||
|
if(!mainMenuContainer)
|
||||||
|
{
|
||||||
|
Debug.LogWarning("Could not find Main Menu Container");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
Debug.Log("Main Menu Container initialized");
|
||||||
|
allTexts = mainMenuContainer.GetComponentsInChildren<TMP_Text>();
|
||||||
|
title = GameObjectTool.FindChildByName(mainMenuContainer.transform, "MainTitle")?.GetComponent<Image>();
|
||||||
|
linkMainMenu = true;
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool SetFont(TMP_FontAsset font)
|
||||||
|
{
|
||||||
|
if(allTexts == null || allTexts.Length == 0)
|
||||||
|
return false;
|
||||||
|
foreach (var text in allTexts)
|
||||||
|
{
|
||||||
|
text.font = font;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool SetTitle(Sprite texture)
|
||||||
|
{
|
||||||
|
titleSprite=texture;
|
||||||
|
if(title==null)
|
||||||
|
return false;
|
||||||
|
title.sprite = texture;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
7
UIFrame/Manager/TextureManager.cs
Normal file
7
UIFrame/Manager/TextureManager.cs
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
namespace UIFrame.Manager
|
||||||
|
{
|
||||||
|
public class TextureManager
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,19 +1,69 @@
|
|||||||
|
using System;
|
||||||
|
using Duckov.Modding;
|
||||||
using Duckov.Options.UI;
|
using Duckov.Options.UI;
|
||||||
|
using Duckov.UI;
|
||||||
|
using Duckov.UI.Animations;
|
||||||
|
using Duckov.Utilities;
|
||||||
|
using HarmonyLib;
|
||||||
using UnityEngine;
|
using UnityEngine;
|
||||||
|
|
||||||
namespace UIFrame
|
namespace UIFrame
|
||||||
{
|
{
|
||||||
public class ModBehaviour:Duckov.Modding.ModBehaviour
|
public class ModBehaviour:Duckov.Modding.ModBehaviour
|
||||||
{
|
{
|
||||||
|
private const string MOD_ID="UIFrame";
|
||||||
|
|
||||||
|
private GameObject? workerObject;
|
||||||
|
|
||||||
|
private Harmony? harmony;
|
||||||
|
|
||||||
protected override void OnAfterSetup()
|
protected override void OnAfterSetup()
|
||||||
{
|
{
|
||||||
Debug.Log("OnAfterSetup");
|
CreateAPIObject();
|
||||||
|
if (harmony == null)
|
||||||
|
{
|
||||||
|
harmony=new HarmonyLib.Harmony(MOD_ID);
|
||||||
|
}
|
||||||
|
harmony.PatchAll();
|
||||||
|
Test();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
protected override void OnBeforeDeactivate()
|
protected override void OnBeforeDeactivate()
|
||||||
{
|
{
|
||||||
Debug.Log("OnBeforeDeactivate");
|
ClearAPIObject();
|
||||||
|
harmony?.UnpatchAll(MOD_ID);
|
||||||
|
harmony = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void CreateAPIObject()
|
||||||
|
{
|
||||||
|
if(workerObject)
|
||||||
|
return;
|
||||||
|
workerObject = new GameObject($"{MOD_ID}_APIObject");
|
||||||
|
workerObject.AddComponent<UIFrameWorker>();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void ClearAPIObject()
|
||||||
|
{
|
||||||
|
if(!workerObject)
|
||||||
|
return;
|
||||||
|
Destroy(workerObject);
|
||||||
|
workerObject = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void Test()
|
||||||
|
{
|
||||||
|
if(!UIFrameAPI.Initialize())
|
||||||
|
return;
|
||||||
|
if (UIFrameAPI.SetGameTitle(@"C:\Users\Lenovo\Pictures\异噬.png"))
|
||||||
|
{
|
||||||
|
Debug.Log("设置标题完成");
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Debug.Log("标题设置失败");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
15
UIFrame/Patch/PatchSceneLoaderLoadMainMenu.cs
Normal file
15
UIFrame/Patch/PatchSceneLoaderLoadMainMenu.cs
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
|
||||||
|
|
||||||
|
using UnityEngine;
|
||||||
|
|
||||||
|
namespace UIFrame.Patch
|
||||||
|
{
|
||||||
|
[HarmonyLib.HarmonyPatch(typeof(SceneLoader), "LoadMainMenu")]
|
||||||
|
public class PatchSceneLoaderLoadMainMenu
|
||||||
|
{
|
||||||
|
public static void Postfix()
|
||||||
|
{
|
||||||
|
Debug.Log("LoadMainMenu called");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
38
UIFrame/Singleton.cs
Normal file
38
UIFrame/Singleton.cs
Normal file
@@ -0,0 +1,38 @@
|
|||||||
|
using System;
|
||||||
|
|
||||||
|
namespace UIFrame
|
||||||
|
{
|
||||||
|
public abstract class Singleton<T> where T : class
|
||||||
|
{
|
||||||
|
private static T? _instance;
|
||||||
|
private static readonly object _lock = new object(); // 用于多线程安全
|
||||||
|
public static T? Instance
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
// 在多线程环境下,确保只有一个线程能够创建实例
|
||||||
|
lock (_lock)
|
||||||
|
{
|
||||||
|
if (_instance == null)
|
||||||
|
{
|
||||||
|
|
||||||
|
_instance = Activator.CreateInstance(typeof(T), true) as T;
|
||||||
|
if (_instance == null)
|
||||||
|
{
|
||||||
|
throw new InvalidOperationException($"无法为类型 {typeof(T).Name} 创建单例实例。" +
|
||||||
|
"请确保它有一个私有的无参构造函数。");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return _instance;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
protected Singleton()
|
||||||
|
{
|
||||||
|
if (_instance != null)
|
||||||
|
{
|
||||||
|
throw new InvalidOperationException($"试图创建第二个单例实例 {typeof(T).Name}。请通过 Singleton<{typeof(T).Name}>.Instance 访问。");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -18,5 +18,8 @@
|
|||||||
<Reference Include="$(DuckovPath)\Duckov_Data\Managed\Unity*" Private="False" />
|
<Reference Include="$(DuckovPath)\Duckov_Data\Managed\Unity*" Private="False" />
|
||||||
<Reference Include="$(DuckovPath)\Duckov_Data\Managed\FMODUnity.dll" Private="False" />
|
<Reference Include="$(DuckovPath)\Duckov_Data\Managed\FMODUnity.dll" Private="False" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
<ItemGroup>
|
||||||
|
<PackageReference Include="Lib.Harmony" Version="2.4.1" />
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
</Project>
|
</Project>
|
||||||
|
|||||||
@@ -1,7 +1,99 @@
|
|||||||
|
using System.Collections.Generic;
|
||||||
|
using UIFrameAPI;
|
||||||
|
using UnityEngine;
|
||||||
|
|
||||||
|
//改为自己的命名空间更好,这是一个画布单元,一个命名空间一个画布
|
||||||
namespace UIFrame
|
namespace UIFrame
|
||||||
{
|
{
|
||||||
public class UIFrameAPI
|
//反射虽然很好用,但我认为用组件传递高效
|
||||||
|
public static class UIFrameAPI
|
||||||
{
|
{
|
||||||
|
private static UIFrameAPIComponent? _apiComponent;
|
||||||
|
private static bool createdCanvas = false;
|
||||||
|
private static readonly string NameSpace = typeof(UIFrameAPI).Namespace ?? "UIFrame";
|
||||||
|
|
||||||
|
public static Dictionary<string, Texture2D> textureCache = new Dictionary<string, Texture2D>();
|
||||||
|
public static Dictionary<string, Sprite> spriteCache = new Dictionary<string, Sprite>();
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 初始化API
|
||||||
|
/// </summary>
|
||||||
|
/// <returns></returns>
|
||||||
|
public static bool Initialize()
|
||||||
|
{
|
||||||
|
if (_apiComponent!=null)
|
||||||
|
return true;
|
||||||
|
_apiComponent = Object.FindObjectOfType<UIFrameAPIComponent>();
|
||||||
|
return _apiComponent;
|
||||||
|
}
|
||||||
|
/// <summary>
|
||||||
|
/// 设置标题图片(游戏中的标题是图片)
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="imageFilePath">图片路径</param>
|
||||||
|
/// <returns></returns>
|
||||||
|
public static bool SetGameTitle(string imageFilePath)
|
||||||
|
{
|
||||||
|
var texture=LoadSprite(imageFilePath);
|
||||||
|
if(texture==null)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return _apiComponent&&_apiComponent.SetTitleImage(texture);
|
||||||
|
}
|
||||||
|
/// <summary>
|
||||||
|
/// 设置标题图片(游戏中的标题是图片)
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="sprite">贴图</param>
|
||||||
|
/// <returns></returns>
|
||||||
|
public static bool SetGameTitle(Sprite sprite)
|
||||||
|
{
|
||||||
|
return _apiComponent&&_apiComponent.SetTitleImage(sprite);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 加载图片文件为Texture2D
|
||||||
|
/// Texture2D实际存储了图片,图片的数据会上传显卡
|
||||||
|
/// 此函数默认加载后不保留内存备份,即不可读像素
|
||||||
|
/// 函数会建立文件到图片的索引缓存
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="imageFilePath"></param>
|
||||||
|
/// <returns></returns>
|
||||||
|
public static Texture2D? LoadImage(string imageFilePath)
|
||||||
|
{
|
||||||
|
if (!textureCache.ContainsKey(imageFilePath))
|
||||||
|
{
|
||||||
|
var texture=_apiComponent.LoadTexture(imageFilePath);
|
||||||
|
if (texture != null)
|
||||||
|
{
|
||||||
|
textureCache[imageFilePath] = texture;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Debug.LogError($"加载图片:{imageFilePath}失败");
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return textureCache[imageFilePath];
|
||||||
|
}
|
||||||
|
/// <summary>
|
||||||
|
/// 加载图片为Sprite
|
||||||
|
/// 直接简单的调用LoadImage再创建一个代表此图片的Sprite
|
||||||
|
/// Sprite只是表明了图片的处理方式,所以为了灵活性建议自己创建
|
||||||
|
/// 此函数会缓存地址到Sprite的索引
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="imageFilePath"></param>
|
||||||
|
/// <returns></returns>
|
||||||
|
public static Sprite? LoadSprite(string imageFilePath)
|
||||||
|
{
|
||||||
|
var texture=LoadImage(imageFilePath);
|
||||||
|
if (texture==null)
|
||||||
|
return null;
|
||||||
|
if (!spriteCache.ContainsKey(imageFilePath))
|
||||||
|
{
|
||||||
|
spriteCache[imageFilePath] = Sprite.Create(texture, new Rect(0, 0, texture.width, texture.height), Vector2.zero);
|
||||||
|
}
|
||||||
|
return spriteCache[imageFilePath];
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
21
UIFrame/UIFrameAPIComponent.cs
Normal file
21
UIFrame/UIFrameAPIComponent.cs
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
using TMPro;
|
||||||
|
using UnityEngine;
|
||||||
|
|
||||||
|
namespace UIFrameAPI
|
||||||
|
{
|
||||||
|
public abstract class UIFrameAPIComponent:MonoBehaviour
|
||||||
|
{
|
||||||
|
public abstract bool CreateCanvas(string name);
|
||||||
|
|
||||||
|
//设置游戏主菜单的原版标题
|
||||||
|
public abstract bool SetTitleImage(Sprite sprite);
|
||||||
|
|
||||||
|
//创建一个TMP字体
|
||||||
|
public abstract TMP_FontAsset CreateFontAsset(string fontFilePath);
|
||||||
|
|
||||||
|
//设置游戏字体
|
||||||
|
public abstract bool SetFont(TMP_FontAsset font);
|
||||||
|
|
||||||
|
public abstract Texture2D? LoadTexture(string imageFilePath);
|
||||||
|
}
|
||||||
|
}
|
||||||
65
UIFrame/UIFrameWorker.cs
Normal file
65
UIFrame/UIFrameWorker.cs
Normal file
@@ -0,0 +1,65 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using TMPro;
|
||||||
|
using UIFrameAPI;
|
||||||
|
using UnityEngine;
|
||||||
|
using UnityEngine.SceneManagement;
|
||||||
|
using UnityEngine.TextCore.LowLevel;
|
||||||
|
|
||||||
|
namespace UIFrame
|
||||||
|
{
|
||||||
|
public class UIFrameWorker : UIFrameAPIComponent
|
||||||
|
{
|
||||||
|
public GameOriginMainMenuUI gameOriginMainMenuUI = new GameOriginMainMenuUI();
|
||||||
|
public Dictionary<string, Canvas> canvasDic = new Dictionary<string, Canvas>();
|
||||||
|
|
||||||
|
|
||||||
|
private void Awake()
|
||||||
|
{
|
||||||
|
gameOriginMainMenuUI.Initialize();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnDestroy()
|
||||||
|
{
|
||||||
|
gameOriginMainMenuUI.Cleanup();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public override bool CreateCanvas(string name)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public override bool SetTitleImage(Sprite sprite)
|
||||||
|
{
|
||||||
|
return gameOriginMainMenuUI.SetTitle(sprite);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public override TMP_FontAsset CreateFontAsset(string fontFilePath)
|
||||||
|
{
|
||||||
|
var font = Font.CreateDynamicFontFromOSFont(fontFilePath, 24);
|
||||||
|
var tmpFont = TMP_FontAsset.CreateFontAsset(
|
||||||
|
font,
|
||||||
|
samplingPointSize: 72, // 采样点大小,影响字体质量
|
||||||
|
atlasPadding: 4, // 图集内字符间距
|
||||||
|
renderMode: GlyphRenderMode.SDFAA, // 推荐使用 SDF 抗锯齿模式
|
||||||
|
atlasWidth: 1024, // 图集宽度 (2的幂)
|
||||||
|
atlasHeight: 1024, // 图集高度 (2的幂)
|
||||||
|
atlasPopulationMode: AtlasPopulationMode.Dynamic, // 动态填充
|
||||||
|
enableMultiAtlasSupport: true // 启用多图集支持
|
||||||
|
);
|
||||||
|
return tmpFont;
|
||||||
|
}
|
||||||
|
|
||||||
|
public override bool SetFont(TMP_FontAsset font)
|
||||||
|
{
|
||||||
|
return gameOriginMainMenuUI.SetFont(font);
|
||||||
|
}
|
||||||
|
|
||||||
|
public override Texture2D? LoadTexture(string imageFilePath)
|
||||||
|
{
|
||||||
|
return Utilities.ImageLoader.LoadImageFromFile(imageFilePath);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,7 +0,0 @@
|
|||||||
namespace UIFrame
|
|
||||||
{
|
|
||||||
public static class UIManager
|
|
||||||
{
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
66
UIFrame/Utilities/GameObjectTool.cs
Normal file
66
UIFrame/Utilities/GameObjectTool.cs
Normal file
@@ -0,0 +1,66 @@
|
|||||||
|
using System.Collections.Generic;
|
||||||
|
using UnityEngine;
|
||||||
|
|
||||||
|
namespace UIFrame.Utilities
|
||||||
|
{
|
||||||
|
public class GameObjectTool
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// 在指定父对象下,查找第一个匹配名称的子GameObject(可以是孙子、曾孙等)
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="parent">要查找的父Transform。</param>
|
||||||
|
/// <param name="name">要查找的GameObject的名称。</param>
|
||||||
|
/// <returns>找到的GameObject,如果未找到则返回null。</returns>
|
||||||
|
public static GameObject? FindChildByName(Transform parent, string name)
|
||||||
|
{
|
||||||
|
// 查找父对象本身是否就是目标对象
|
||||||
|
if (parent.name.Equals(name))
|
||||||
|
{
|
||||||
|
return parent.gameObject;
|
||||||
|
}
|
||||||
|
// 遍历所有直接子对象
|
||||||
|
foreach (Transform child in parent)
|
||||||
|
{
|
||||||
|
// 检查当前子对象是否是目标对象
|
||||||
|
if (child.name.Equals(name))
|
||||||
|
{
|
||||||
|
return child.gameObject;
|
||||||
|
}
|
||||||
|
// 递归查找子对象的子对象
|
||||||
|
var found = FindChildByName(child, name);
|
||||||
|
if (found)
|
||||||
|
{
|
||||||
|
return found;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
/// <summary>
|
||||||
|
/// 在指定父对象下,查找所有匹配名称的子GameObject(可以是孙子、曾孙等),不区分大小写。
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="parent">要查找的父Transform。</param>
|
||||||
|
/// <param name="name">要查找的GameObject的名称。</param>
|
||||||
|
/// <returns>所有找到的GameObject列表。</returns>
|
||||||
|
public static List<GameObject> FindChildrenByName(Transform parent, string name)
|
||||||
|
{
|
||||||
|
var foundObjects = new List<GameObject>();
|
||||||
|
FindChildrenByNameRecursive(parent, name, foundObjects);
|
||||||
|
return foundObjects;
|
||||||
|
}
|
||||||
|
private static void FindChildrenByNameRecursive(Transform currentTransform, string name, List<GameObject> foundObjects)
|
||||||
|
{
|
||||||
|
// 检查当前对象是否是目标对象
|
||||||
|
if (currentTransform.name.Equals(name))
|
||||||
|
{
|
||||||
|
foundObjects.Add(currentTransform.gameObject);
|
||||||
|
}
|
||||||
|
// 遍历所有子对象并递归查找
|
||||||
|
foreach (Transform child in currentTransform)
|
||||||
|
{
|
||||||
|
FindChildrenByNameRecursive(child, name, foundObjects);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
67
UIFrame/Utilities/ImageLoader.cs
Normal file
67
UIFrame/Utilities/ImageLoader.cs
Normal file
@@ -0,0 +1,67 @@
|
|||||||
|
using System;
|
||||||
|
using System.IO;
|
||||||
|
using UnityEngine;
|
||||||
|
|
||||||
|
namespace UIFrame.Utilities
|
||||||
|
{
|
||||||
|
public class ImageLoader
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// 从指定文件路径加载图片并创建 Texture2D。
|
||||||
|
/// 支持常用的图片格式 (如 .png, .jpg, .jpeg, .bmp, .tga)
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="filePath">图片文件的绝对路径。</param>
|
||||||
|
/// <param name="createNewTexture">如果为true,则创建一个新的Texture2D对象并加载图片数据。如果为false,它会尝试加载到默认的空白Texture2D对象上,但通常建议使用true。</param>
|
||||||
|
/// <param name="linear">指定纹理是否加载为线性颜色空间 (true) 或 sRGB 颜色空间 (false)。</param>
|
||||||
|
/// <returns>加载成功的 Texture2D 对象,如果失败则返回 null。</returns>
|
||||||
|
public static Texture2D? LoadImageFromFile(string filePath, bool createNewTexture = true, bool linear = false)
|
||||||
|
{
|
||||||
|
if (string.IsNullOrEmpty(filePath))
|
||||||
|
{
|
||||||
|
Debug.LogError("ImageLoader: 图片文件路径为空或无效。");
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!File.Exists(filePath))
|
||||||
|
{
|
||||||
|
Debug.LogError($"ImageLoader: 文件不存在于路径: {filePath}");
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
Texture2D? texture = null;
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var fileData = File.ReadAllBytes(filePath);
|
||||||
|
if (createNewTexture)
|
||||||
|
{
|
||||||
|
texture = new Texture2D(2, 2, TextureFormat.RGBA32, false, linear);
|
||||||
|
}
|
||||||
|
else if (texture == null)
|
||||||
|
{
|
||||||
|
Debug.LogError("ImageLoader: 未能提供现有Texture2D用于加载,且createNewTexture为false。");
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
var success = texture.LoadImage(fileData, true);
|
||||||
|
|
||||||
|
if (!success)
|
||||||
|
{
|
||||||
|
Debug.LogError($"ImageLoader: 无法加载图片数据到Texture2D。请检查文件是否为有效的图片格式或是否损坏: {filePath}");
|
||||||
|
UnityEngine.Object.Destroy(texture); // 销毁失败的纹理对象
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
return texture;
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
Debug.LogError($"ImageLoader: 加载图片时发生错误: {filePath} - {ex.Message}");
|
||||||
|
if (texture != null)
|
||||||
|
{
|
||||||
|
UnityEngine.Object.Destroy(texture); // 发生异常时销毁已创建的纹理
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -13,7 +13,7 @@ using System.Reflection;
|
|||||||
[assembly: System.Reflection.AssemblyCompanyAttribute("UIFrame")]
|
[assembly: System.Reflection.AssemblyCompanyAttribute("UIFrame")]
|
||||||
[assembly: System.Reflection.AssemblyConfigurationAttribute("Release")]
|
[assembly: System.Reflection.AssemblyConfigurationAttribute("Release")]
|
||||||
[assembly: System.Reflection.AssemblyFileVersionAttribute("1.0.0.0")]
|
[assembly: System.Reflection.AssemblyFileVersionAttribute("1.0.0.0")]
|
||||||
[assembly: System.Reflection.AssemblyInformationalVersionAttribute("1.0.0+0206a83f56b5a794fe2f173b4a047cc4f0d4cd90")]
|
[assembly: System.Reflection.AssemblyInformationalVersionAttribute("1.0.0+5d69efbc3f80a5422cef0884e02fb27adf20b467")]
|
||||||
[assembly: System.Reflection.AssemblyProductAttribute("UIFrame")]
|
[assembly: System.Reflection.AssemblyProductAttribute("UIFrame")]
|
||||||
[assembly: System.Reflection.AssemblyTitleAttribute("UIFrame")]
|
[assembly: System.Reflection.AssemblyTitleAttribute("UIFrame")]
|
||||||
[assembly: System.Reflection.AssemblyVersionAttribute("1.0.0.0")]
|
[assembly: System.Reflection.AssemblyVersionAttribute("1.0.0.0")]
|
||||||
|
|||||||
@@ -1 +1 @@
|
|||||||
eb1c4a5bd10a6004eb2efc2bf4354383e0389ddf5e8c9585686a601d75e8a73a
|
2fde23c3fee254f6fc8f4689686f8631f1cec9f5b900327d5b657c6830a03267
|
||||||
|
|||||||
Binary file not shown.
Binary file not shown.
@@ -1 +1 @@
|
|||||||
26cd918e8bf85dee638761fbef4e2117b1734f804498932e0dc10264a1740877
|
8df4ddcbe4b7016ad51bf9e0b9ea8b606b36f4d0bbae7f1650b893a49036873e
|
||||||
|
|||||||
Binary file not shown.
@@ -49,6 +49,12 @@
|
|||||||
"frameworks": {
|
"frameworks": {
|
||||||
"netstandard2.1": {
|
"netstandard2.1": {
|
||||||
"targetAlias": "netstandard2.1",
|
"targetAlias": "netstandard2.1",
|
||||||
|
"dependencies": {
|
||||||
|
"Lib.Harmony": {
|
||||||
|
"target": "Package",
|
||||||
|
"version": "[2.4.1, )"
|
||||||
|
}
|
||||||
|
},
|
||||||
"imports": [
|
"imports": [
|
||||||
"net461",
|
"net461",
|
||||||
"net462",
|
"net462",
|
||||||
|
|||||||
@@ -1,11 +1,165 @@
|
|||||||
{
|
{
|
||||||
"version": 3,
|
"version": 3,
|
||||||
"targets": {
|
"targets": {
|
||||||
".NETStandard,Version=v2.1": {}
|
".NETStandard,Version=v2.1": {
|
||||||
|
"Lib.Harmony/2.4.1": {
|
||||||
|
"type": "package",
|
||||||
|
"dependencies": {
|
||||||
|
"Lib.Harmony.Ref": "2.4.1"
|
||||||
|
},
|
||||||
|
"compile": {
|
||||||
|
"lib/netstandard2.0/_._": {}
|
||||||
|
},
|
||||||
|
"runtime": {
|
||||||
|
"lib/netstandard2.0/_._": {}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"Lib.Harmony.Ref/2.4.1": {
|
||||||
|
"type": "package",
|
||||||
|
"dependencies": {
|
||||||
|
"System.Reflection.Emit": "4.7.0"
|
||||||
|
},
|
||||||
|
"compile": {
|
||||||
|
"ref/netstandard2.0/0Harmony.dll": {
|
||||||
|
"related": ".xml"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"System.Reflection.Emit/4.7.0": {
|
||||||
|
"type": "package",
|
||||||
|
"compile": {
|
||||||
|
"ref/netstandard2.1/_._": {}
|
||||||
|
},
|
||||||
|
"runtime": {
|
||||||
|
"lib/netstandard2.1/_._": {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"libraries": {
|
||||||
|
"Lib.Harmony/2.4.1": {
|
||||||
|
"sha512": "iLTZi/kKKB18jYEIwReZSx2xXyVUh4J1swReMgvYBBBn4tzA1Nd0PJlVyntY5BDdSiXSxzmvjc/3OYfFs0YwFg==",
|
||||||
|
"type": "package",
|
||||||
|
"path": "lib.harmony/2.4.1",
|
||||||
|
"files": [
|
||||||
|
".nupkg.metadata",
|
||||||
|
".signature.p7s",
|
||||||
|
"HarmonyLogo.png",
|
||||||
|
"LICENSE",
|
||||||
|
"README.md",
|
||||||
|
"lib.harmony.2.4.1.nupkg.sha512",
|
||||||
|
"lib.harmony.nuspec",
|
||||||
|
"lib/net35/0Harmony.dll",
|
||||||
|
"lib/net35/0Harmony.pdb",
|
||||||
|
"lib/net35/0Harmony.xml",
|
||||||
|
"lib/net452/0Harmony.dll",
|
||||||
|
"lib/net452/0Harmony.pdb",
|
||||||
|
"lib/net452/0Harmony.xml",
|
||||||
|
"lib/net472/0Harmony.dll",
|
||||||
|
"lib/net472/0Harmony.pdb",
|
||||||
|
"lib/net472/0Harmony.xml",
|
||||||
|
"lib/net48/0Harmony.dll",
|
||||||
|
"lib/net48/0Harmony.pdb",
|
||||||
|
"lib/net48/0Harmony.xml",
|
||||||
|
"lib/net5.0/0Harmony.dll",
|
||||||
|
"lib/net5.0/0Harmony.pdb",
|
||||||
|
"lib/net5.0/0Harmony.xml",
|
||||||
|
"lib/net6.0/0Harmony.dll",
|
||||||
|
"lib/net6.0/0Harmony.pdb",
|
||||||
|
"lib/net6.0/0Harmony.xml",
|
||||||
|
"lib/net7.0/0Harmony.dll",
|
||||||
|
"lib/net7.0/0Harmony.pdb",
|
||||||
|
"lib/net7.0/0Harmony.xml",
|
||||||
|
"lib/net8.0/0Harmony.dll",
|
||||||
|
"lib/net8.0/0Harmony.pdb",
|
||||||
|
"lib/net8.0/0Harmony.xml",
|
||||||
|
"lib/net9.0/0Harmony.dll",
|
||||||
|
"lib/net9.0/0Harmony.pdb",
|
||||||
|
"lib/net9.0/0Harmony.xml",
|
||||||
|
"lib/netcoreapp3.0/0Harmony.dll",
|
||||||
|
"lib/netcoreapp3.0/0Harmony.pdb",
|
||||||
|
"lib/netcoreapp3.0/0Harmony.xml",
|
||||||
|
"lib/netcoreapp3.1/0Harmony.dll",
|
||||||
|
"lib/netcoreapp3.1/0Harmony.pdb",
|
||||||
|
"lib/netcoreapp3.1/0Harmony.xml",
|
||||||
|
"lib/netstandard2.0/_._"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"Lib.Harmony.Ref/2.4.1": {
|
||||||
|
"sha512": "+u1y2Qd6OlSUQ8JtrsrSo3adnAsrXMJ2YPYtbW+FAmdPI5yw34M9VX4bKl8ZwRuUzaGzZIz+oGMbn/yS4fWtZw==",
|
||||||
|
"type": "package",
|
||||||
|
"path": "lib.harmony.ref/2.4.1",
|
||||||
|
"files": [
|
||||||
|
".nupkg.metadata",
|
||||||
|
".signature.p7s",
|
||||||
|
"HarmonyLogo.png",
|
||||||
|
"LICENSE",
|
||||||
|
"README.md",
|
||||||
|
"lib.harmony.ref.2.4.1.nupkg.sha512",
|
||||||
|
"lib.harmony.ref.nuspec",
|
||||||
|
"ref/netstandard2.0/0Harmony.dll",
|
||||||
|
"ref/netstandard2.0/0Harmony.xml"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"System.Reflection.Emit/4.7.0": {
|
||||||
|
"sha512": "VR4kk8XLKebQ4MZuKuIni/7oh+QGFmZW3qORd1GvBq/8026OpW501SzT/oypwiQl4TvT8ErnReh/NzY9u+C6wQ==",
|
||||||
|
"type": "package",
|
||||||
|
"path": "system.reflection.emit/4.7.0",
|
||||||
|
"files": [
|
||||||
|
".nupkg.metadata",
|
||||||
|
".signature.p7s",
|
||||||
|
"LICENSE.TXT",
|
||||||
|
"THIRD-PARTY-NOTICES.TXT",
|
||||||
|
"lib/MonoAndroid10/_._",
|
||||||
|
"lib/MonoTouch10/_._",
|
||||||
|
"lib/net45/_._",
|
||||||
|
"lib/netcore50/System.Reflection.Emit.dll",
|
||||||
|
"lib/netcoreapp2.0/_._",
|
||||||
|
"lib/netstandard1.1/System.Reflection.Emit.dll",
|
||||||
|
"lib/netstandard1.1/System.Reflection.Emit.xml",
|
||||||
|
"lib/netstandard1.3/System.Reflection.Emit.dll",
|
||||||
|
"lib/netstandard2.0/System.Reflection.Emit.dll",
|
||||||
|
"lib/netstandard2.0/System.Reflection.Emit.xml",
|
||||||
|
"lib/netstandard2.1/_._",
|
||||||
|
"lib/xamarinios10/_._",
|
||||||
|
"lib/xamarinmac20/_._",
|
||||||
|
"lib/xamarintvos10/_._",
|
||||||
|
"lib/xamarinwatchos10/_._",
|
||||||
|
"ref/MonoAndroid10/_._",
|
||||||
|
"ref/MonoTouch10/_._",
|
||||||
|
"ref/net45/_._",
|
||||||
|
"ref/netcoreapp2.0/_._",
|
||||||
|
"ref/netstandard1.1/System.Reflection.Emit.dll",
|
||||||
|
"ref/netstandard1.1/System.Reflection.Emit.xml",
|
||||||
|
"ref/netstandard1.1/de/System.Reflection.Emit.xml",
|
||||||
|
"ref/netstandard1.1/es/System.Reflection.Emit.xml",
|
||||||
|
"ref/netstandard1.1/fr/System.Reflection.Emit.xml",
|
||||||
|
"ref/netstandard1.1/it/System.Reflection.Emit.xml",
|
||||||
|
"ref/netstandard1.1/ja/System.Reflection.Emit.xml",
|
||||||
|
"ref/netstandard1.1/ko/System.Reflection.Emit.xml",
|
||||||
|
"ref/netstandard1.1/ru/System.Reflection.Emit.xml",
|
||||||
|
"ref/netstandard1.1/zh-hans/System.Reflection.Emit.xml",
|
||||||
|
"ref/netstandard1.1/zh-hant/System.Reflection.Emit.xml",
|
||||||
|
"ref/netstandard2.0/System.Reflection.Emit.dll",
|
||||||
|
"ref/netstandard2.0/System.Reflection.Emit.xml",
|
||||||
|
"ref/netstandard2.1/_._",
|
||||||
|
"ref/xamarinios10/_._",
|
||||||
|
"ref/xamarinmac20/_._",
|
||||||
|
"ref/xamarintvos10/_._",
|
||||||
|
"ref/xamarinwatchos10/_._",
|
||||||
|
"runtimes/aot/lib/netcore50/System.Reflection.Emit.dll",
|
||||||
|
"runtimes/aot/lib/netcore50/System.Reflection.Emit.xml",
|
||||||
|
"system.reflection.emit.4.7.0.nupkg.sha512",
|
||||||
|
"system.reflection.emit.nuspec",
|
||||||
|
"useSharedDesignerContext.txt",
|
||||||
|
"version.txt"
|
||||||
|
]
|
||||||
|
}
|
||||||
},
|
},
|
||||||
"libraries": {},
|
|
||||||
"projectFileDependencyGroups": {
|
"projectFileDependencyGroups": {
|
||||||
".NETStandard,Version=v2.1": []
|
".NETStandard,Version=v2.1": [
|
||||||
|
"Lib.Harmony >= 2.4.1"
|
||||||
|
]
|
||||||
},
|
},
|
||||||
"packageFolders": {
|
"packageFolders": {
|
||||||
"C:\\Users\\Lenovo\\.nuget\\packages\\": {},
|
"C:\\Users\\Lenovo\\.nuget\\packages\\": {},
|
||||||
@@ -56,6 +210,12 @@
|
|||||||
"frameworks": {
|
"frameworks": {
|
||||||
"netstandard2.1": {
|
"netstandard2.1": {
|
||||||
"targetAlias": "netstandard2.1",
|
"targetAlias": "netstandard2.1",
|
||||||
|
"dependencies": {
|
||||||
|
"Lib.Harmony": {
|
||||||
|
"target": "Package",
|
||||||
|
"version": "[2.4.1, )"
|
||||||
|
}
|
||||||
|
},
|
||||||
"imports": [
|
"imports": [
|
||||||
"net461",
|
"net461",
|
||||||
"net462",
|
"net462",
|
||||||
|
|||||||
@@ -1,8 +1,12 @@
|
|||||||
{
|
{
|
||||||
"version": 2,
|
"version": 2,
|
||||||
"dgSpecHash": "A4ZumVBkozg=",
|
"dgSpecHash": "b0JcKXS9rW0=",
|
||||||
"success": true,
|
"success": true,
|
||||||
"projectFilePath": "D:\\vs_project\\DuckovMods\\UIFrame\\UIFrame.csproj",
|
"projectFilePath": "D:\\vs_project\\DuckovMods\\UIFrame\\UIFrame.csproj",
|
||||||
"expectedPackageFiles": [],
|
"expectedPackageFiles": [
|
||||||
|
"C:\\Users\\Lenovo\\.nuget\\packages\\lib.harmony\\2.4.1\\lib.harmony.2.4.1.nupkg.sha512",
|
||||||
|
"C:\\Users\\Lenovo\\.nuget\\packages\\lib.harmony.ref\\2.4.1\\lib.harmony.ref.2.4.1.nupkg.sha512",
|
||||||
|
"C:\\Users\\Lenovo\\.nuget\\packages\\system.reflection.emit\\4.7.0\\system.reflection.emit.4.7.0.nupkg.sha512"
|
||||||
|
],
|
||||||
"logs": []
|
"logs": []
|
||||||
}
|
}
|
||||||
@@ -1 +1 @@
|
|||||||
"restore":{"projectUniqueName":"D:\\vs_project\\DuckovMods\\UIFrame\\UIFrame.csproj","projectName":"UIFrame","projectPath":"D:\\vs_project\\DuckovMods\\UIFrame\\UIFrame.csproj","outputPath":"D:\\vs_project\\DuckovMods\\UIFrame\\obj\\","projectStyle":"PackageReference","fallbackFolders":["D:\\vsShare\\NuGetPackages"],"originalTargetFrameworks":["netstandard2.1"],"sources":{"C:\\Program Files (x86)\\Microsoft SDKs\\NuGetPackages\\":{},"https://api.nuget.org/v3/index.json":{}},"frameworks":{"netstandard2.1":{"targetAlias":"netstandard2.1","projectReferences":{}}},"warningProperties":{"warnAsError":["NU1605"]},"restoreAuditProperties":{"enableAudit":"true","auditLevel":"low","auditMode":"direct"},"SdkAnalysisLevel":"9.0.300"}"frameworks":{"netstandard2.1":{"targetAlias":"netstandard2.1","imports":["net461","net462","net47","net471","net472","net48","net481"],"assetTargetFallback":true,"warn":true,"frameworkReferences":{"NETStandard.Library":{"privateAssets":"all"}},"runtimeIdentifierGraphPath":"C:\\Program Files\\dotnet\\sdk\\9.0.306\\RuntimeIdentifierGraph.json"}}
|
"restore":{"projectUniqueName":"D:\\vs_project\\DuckovMods\\UIFrame\\UIFrame.csproj","projectName":"UIFrame","projectPath":"D:\\vs_project\\DuckovMods\\UIFrame\\UIFrame.csproj","outputPath":"D:\\vs_project\\DuckovMods\\UIFrame\\obj\\","projectStyle":"PackageReference","fallbackFolders":["D:\\vsShare\\NuGetPackages"],"originalTargetFrameworks":["netstandard2.1"],"sources":{"C:\\Program Files (x86)\\Microsoft SDKs\\NuGetPackages\\":{},"https://api.nuget.org/v3/index.json":{}},"frameworks":{"netstandard2.1":{"targetAlias":"netstandard2.1","projectReferences":{}}},"warningProperties":{"warnAsError":["NU1605"]},"restoreAuditProperties":{"enableAudit":"true","auditLevel":"low","auditMode":"direct"},"SdkAnalysisLevel":"9.0.300"}"frameworks":{"netstandard2.1":{"targetAlias":"netstandard2.1","dependencies":{"Lib.Harmony":{"target":"Package","version":"[2.4.1, )"}},"imports":["net461","net462","net47","net471","net472","net48","net481"],"assetTargetFallback":true,"warn":true,"frameworkReferences":{"NETStandard.Library":{"privateAssets":"all"}},"runtimeIdentifierGraphPath":"C:\\Program Files\\dotnet\\sdk\\9.0.306\\RuntimeIdentifierGraph.json"}}
|
||||||
@@ -1 +1 @@
|
|||||||
17620796287866771
|
17623343068138064
|
||||||
Reference in New Issue
Block a user