Project Path: arc_000-aki-000_GameDebugMenu_d0sro_1u Source Tree: ```txt arc_000-aki-000_GameDebugMenu_d0sro_1u ├── LICENSE ├── Plugin │ └── GameDebugMenu │ ├── Config │ │ └── DefaultGameDebugMenu.ini │ ├── Content │ │ ├── BPM_GDM_WidgetEvents.uasset │ │ ├── BP_GDM_Manager.uasset │ │ ├── DA_GDM_ConsoleCommandSet.uasset │ │ ├── DA_GDM_Manager.uasset │ │ ├── DA_GDM_Master.uasset │ │ ├── Input │ │ │ ├── BP_GDMDebugCameraInput.uasset │ │ │ ├── IA_GameDebugMenu_Camera_OrbitHitPoint.uasset │ │ │ ├── IA_GameDebugMenu_Camera_PawnTeleport.uasset │ │ │ ├── IA_GameDebugMenu_Cancel.uasset │ │ │ ├── IA_GameDebugMenu_Decide.uasset │ │ │ ├── IA_GameDebugMenu_Down.uasset │ │ │ ├── IA_GameDebugMenu_Favorite.uasset │ │ │ ├── IA_GameDebugMenu_Left.uasset │ │ │ ├── IA_GameDebugMenu_OpenCloseMenu.uasset │ │ │ ├── IA_GameDebugMenu_Right.uasset │ │ │ ├── IA_GameDebugMenu_ShowReport.uasset │ │ │ ├── IA_GameDebugMenu_Up.uasset │ │ │ ├── IMC_GameDebugMenu_Camera.uasset │ │ │ ├── IMC_GameDebugMenu_Manager.uasset │ │ │ └── IMC_GameDebugMenu_Widget.uasset │ │ ├── Misc │ │ │ ├── GDMCurve_Choice.uasset │ │ │ ├── TX_GDM_Close.uasset │ │ │ ├── TX_GDM_Grab.uasset │ │ │ ├── TX_GDM_Pin.uasset │ │ │ └── TX_GDM_Triangle.uasset │ │ ├── StringTable │ │ │ ├── ST_GDM_English.uasset │ │ │ └── ST_GDM_Japanese.uasset │ │ └── UI │ │ ├── Base │ │ │ ├── CategoryMenu │ │ │ │ ├── WB_GDM_CategoryList.uasset │ │ │ │ └── WB_GDM_CategoryMenu.uasset │ │ │ ├── WB_GDM_DebugMenuRoot.uasset │ │ │ └── WB_GDM_RootListButton.uasset │ │ ├── ConsoleCommand │ │ │ ├── WB_GDM_ConsoleCommandBtnBase.uasset │ │ │ ├── WB_GDM_ConsoleCommandBtn_Number.uasset │ │ │ ├── WB_GDM_ConsoleCommandButton.uasset │ │ │ ├── WB_GDM_ConsoleCommandCategoryList.uasset │ │ │ └── WB_GDM_ConsoleCommandMenu.uasset │ │ ├── DebugReport │ │ │ ├── WB_GDM_ReportMenu.uasset │ │ │ └── WB_GDM_ReportTitle.uasset │ │ ├── Favorite │ │ │ ├── BP_GDMFavoriteItemDef_ConsoleCommandBase.uasset │ │ │ ├── BP_GDMFavoriteItemDef_ConsoleCommandGroup.uasset │ │ │ ├── BP_GDMFavoriteItemDef_ConsoleCommandNumber.uasset │ │ │ ├── BP_GDMFavoriteItemDef_ConsoleCommandPair.uasset │ │ │ ├── BP_GDMFavoriteItemDef_ConsoleCommandSingle.uasset │ │ │ ├── BP_GDMFavoriteItemDef_Function.uasset │ │ │ ├── BP_GDMFavoriteItemDef_PropertyBase.uasset │ │ │ ├── BP_GDMFavoriteItemDef_PropertyBool.uasset │ │ │ ├── BP_GDMFavoriteItemDef_PropertyByte.uasset │ │ │ ├── BP_GDMFavoriteItemDef_PropertyEnum.uasset │ │ │ ├── BP_GDMFavoriteItemDef_PropertyFloat.uasset │ │ │ ├── BP_GDMFavoriteItemDef_PropertyInt.uasset │ │ │ ├── BP_GDMFavoriteItemDef_PropertyRotator.uasset │ │ │ ├── BP_GDMFavoriteItemDef_PropertyString.uasset │ │ │ ├── BP_GDMFavoriteItemDef_PropertyVector.uasset │ │ │ ├── BP_GDMFavoriteItemDef_PropertyVector2D.uasset │ │ │ └── WB_GDM_FavoriteMenu.uasset │ │ ├── Gameplay │ │ │ ├── Functions │ │ │ │ └── WB_GDM_FunctionButton.uasset │ │ │ ├── Properties │ │ │ │ ├── ST_GDM_UnusedPropertiesInfo.uasset │ │ │ │ ├── WB_GDM_PropertyBool.uasset │ │ │ │ ├── WB_GDM_PropertyButton.uasset │ │ │ │ ├── WB_GDM_PropertyByte.uasset │ │ │ │ ├── WB_GDM_PropertyEnum.uasset │ │ │ │ ├── WB_GDM_PropertyFloat.uasset │ │ │ │ ├── WB_GDM_PropertyInteger.uasset │ │ │ │ ├── WB_GDM_PropertyRotator.uasset │ │ │ │ ├── WB_GDM_PropertyString.uasset │ │ │ │ ├── WB_GDM_PropertyVector.uasset │ │ │ │ └── WB_GDM_PropertyVector2D.uasset │ │ │ ├── WB_GDM_GameplayCategoryList.uasset │ │ │ └── WB_GDM_GameplayMenu.uasset │ │ ├── GrabViewer │ │ │ ├── GDM_Grab_DragWidget.uasset │ │ │ ├── WB_GDM_GrabViewHandle.uasset │ │ │ └── WB_GDM_GrabViewer.uasset │ │ ├── Interfaces │ │ │ ├── BI_GDM_ButtonChoiceAnimation.uasset │ │ │ ├── BI_GDM_ButtonFavorite.uasset │ │ │ └── BI_GDM_ListWidgetsControlEvent.uasset │ │ ├── Localization │ │ │ ├── WB_GDM_LocalizationButtonBase.uasset │ │ │ ├── WB_GDM_LocalizationButtonCulture.uasset │ │ │ ├── WB_GDM_LocalizationButtonLanguage.uasset │ │ │ ├── WB_GDM_LocalizationButtonLocale.uasset │ │ │ ├── WB_GDM_LocalizationCategoryList.uasset │ │ │ └── WB_GDM_LocalizationMenu.uasset │ │ ├── Parts │ │ │ ├── BP_GDM_PadInputWidgetController.uasset │ │ │ ├── WB_GDM_Button.uasset │ │ │ ├── WB_GDM_CheckBox.uasset │ │ │ ├── WB_GDM_Choice.uasset │ │ │ ├── WB_GDM_ComboBoxString.uasset │ │ │ ├── WB_GDM_FavoritePin.uasset │ │ │ └── WB_GDM_IntSpinBox.uasset │ │ ├── Setting │ │ │ ├── WB_GDM_SettingButtonBase.uasset │ │ │ ├── WB_GDM_SettingButton_DeleteSave.uasset │ │ │ ├── WB_GDM_SettingButton_LanguageKeys.uasset │ │ │ ├── WB_GDM_SettingCategoryList.uasset │ │ │ └── WB_GDM_SettingMenu.uasset │ │ └── WB_GDM_Document.uasset │ ├── GameDebugMenu.uplugin │ ├── LICENSE │ ├── README.md │ ├── Resources │ │ └── Icon128.png │ └── Source │ ├── GameDebugMenu │ │ ├── GameDebugMenu.Build.cs │ │ ├── Private │ │ │ ├── Component │ │ │ │ ├── GDMListenerComponent.cpp │ │ │ │ ├── GDMLocalizeStringComponent.cpp │ │ │ │ ├── GDMPlayerControllerProxyComponent.cpp │ │ │ │ ├── GDMPropertyJsonSystemComponent.cpp │ │ │ │ ├── GDMSaveSystemComponent.cpp │ │ │ │ └── GDMScreenshotRequesterComponent.cpp │ │ │ ├── ConsoleCommand │ │ │ │ ├── GDMConsoleCommandValueProvider.cpp │ │ │ │ ├── GDMConsoleCommandValueProviderComponent.cpp │ │ │ │ ├── GDMConsoleVariableCommandValueProvider.cpp │ │ │ │ └── GDMSlomoCommandValueProvider.cpp │ │ │ ├── Data │ │ │ │ ├── GDMConsoleCommandSetAsset.cpp │ │ │ │ ├── GameDebugMenuManagerAsset.cpp │ │ │ │ └── GameDebugMenuMasterAsset.cpp │ │ │ ├── Favorite │ │ │ │ ├── GDMFavoriteItemDefinition.cpp │ │ │ │ └── GDMFavoriteSystemComponent.cpp │ │ │ ├── GameDebugMenu.cpp │ │ │ ├── GameDebugMenuFunctions.cpp │ │ │ ├── GameDebugMenuManager.cpp │ │ │ ├── GameDebugMenuSettings.cpp │ │ │ ├── GameDebugMenuTypes.cpp │ │ │ ├── Input │ │ │ │ ├── GDMDebugCameraInput.cpp │ │ │ │ ├── GDMEnhancedInputComponent.cpp │ │ │ │ ├── GDMInputSystemComponent.cpp │ │ │ │ ├── GDMInputTriggerPulseWithDelay.cpp │ │ │ │ └── GDMPadInputWidgetController.cpp │ │ │ ├── Log │ │ │ │ └── GDMOutputDevice.cpp │ │ │ ├── Reports │ │ │ │ ├── GDMDebugReportRequester.cpp │ │ │ │ ├── GDMRequesterJira.cpp │ │ │ │ ├── GDMRequesterRedmine.cpp │ │ │ │ └── GDMRequesterTrello.cpp │ │ │ └── Widgets │ │ │ ├── GDMButton.cpp │ │ │ ├── GDMComboBoxString.cpp │ │ │ ├── GDMDebugReportWidget.cpp │ │ │ ├── GDMFunctionWidget.cpp │ │ │ ├── GDMIntSpinBox.cpp │ │ │ ├── GDMPropertyWidget.cpp │ │ │ ├── GDMTextBlock.cpp │ │ │ ├── GameDebugMenuRootWidget.cpp │ │ │ └── GameDebugMenuWidget.cpp │ │ └── Public │ │ ├── Component │ │ │ ├── GDMListenerComponent.h │ │ │ ├── GDMLocalizeStringComponent.h │ │ │ ├── GDMPlayerControllerProxyComponent.h │ │ │ ├── GDMPropertyJsonSystemComponent.h │ │ │ ├── GDMSaveSystemComponent.h │ │ │ └── GDMScreenshotRequesterComponent.h │ │ ├── ConsoleCommand │ │ │ ├── GDMConsoleCommandValueProvider.h │ │ │ ├── GDMConsoleCommandValueProviderComponent.h │ │ │ ├── GDMConsoleVariableCommandValueProvider.h │ │ │ └── GDMSlomoCommandValueProvider.h │ │ ├── Data │ │ │ ├── GDMConsoleCommandSetAsset.h │ │ │ ├── GameDebugMenuManagerAsset.h │ │ │ └── GameDebugMenuMasterAsset.h │ │ ├── Favorite │ │ │ ├── GDMFavoriteItemDefinition.h │ │ │ └── GDMFavoriteSystemComponent.h │ │ ├── GameDebugMenu.h │ │ ├── GameDebugMenuFunctions.h │ │ ├── GameDebugMenuManager.h │ │ ├── GameDebugMenuSettings.h │ │ ├── GameDebugMenuTypes.h │ │ ├── Input │ │ │ ├── GDMDebugCameraInput.h │ │ │ ├── GDMEnhancedInputComponent.h │ │ │ ├── GDMInputSystemComponent.h │ │ │ ├── GDMInputTriggerPulseWithDelay.h │ │ │ └── GDMPadInputWidgetController.h │ │ ├── Log │ │ │ └── GDMOutputDevice.h │ │ ├── Reports │ │ │ ├── GDMDebugReportRequester.h │ │ │ ├── GDMRequesterJira.h │ │ │ ├── GDMRequesterRedmine.h │ │ │ └── GDMRequesterTrello.h │ │ └── Widgets │ │ ├── GDMButton.h │ │ ├── GDMComboBoxString.h │ │ ├── GDMDebugReportWidget.h │ │ ├── GDMFunctionWidget.h │ │ ├── GDMIntSpinBox.h │ │ ├── GDMPropertyWidget.h │ │ ├── GDMTextBlock.h │ │ ├── GameDebugMenuRootWidget.h │ │ └── GameDebugMenuWidget.h │ └── GameDebugMenuEditor │ ├── GameDebugMenuEditor.Build.cs │ ├── Private │ │ ├── AssetTypeActions │ │ │ ├── AssetTypeActions_GDMPlayerControllerProxyComponent.cpp │ │ │ ├── AssetTypeActions_GameDebugMenuManager.cpp │ │ │ └── AssetTypeActions_GameDebugMenuWidget.cpp │ │ ├── DetailCustomizations │ │ │ ├── GDMGameplayCategoryKeyCustomization.cpp │ │ │ └── GDMGameplayCategoryKeyCustomization.h │ │ ├── Factory │ │ │ ├── GDMPlayerControllerProxyComponentFactory.cpp │ │ │ ├── GameDebugMenuManagerFactory.cpp │ │ │ └── GameDebugMenuWidgetFactory.cpp │ │ ├── GameDebugMenuEditor.cpp │ │ └── Pins │ │ ├── GDMGameplayCategoryKeyPin.cpp │ │ ├── GDMGameplayCategoryKeyPin.h │ │ └── GDMGameplayCategoryKeyPinFactory.h │ └── Public │ ├── AssetTypeActions │ │ ├── AssetTypeActions_GDMPlayerControllerProxyComponent.h │ │ ├── AssetTypeActions_GameDebugMenuManager.h │ │ └── AssetTypeActions_GameDebugMenuWidget.h │ ├── Factory │ │ ├── GDMPlayerControllerProxyComponentFactory.h │ │ ├── GameDebugMenuManagerFactory.h │ │ └── GameDebugMenuWidgetFactory.h │ └── GameDebugMenuEditor.h ├── README.md └── SampleProject ├── Config │ ├── DefaultEditor.ini │ ├── DefaultEditorPerProjectUserSettings.ini │ ├── DefaultEngine.ini │ ├── DefaultGame.ini │ ├── DefaultGameDebugMenu.ini │ ├── DefaultInput.ini │ └── HoloLens │ └── HoloLensEngine.ini ├── Content │ ├── Sample │ │ ├── BP_SamplePlayerController.uasset │ │ ├── Debug │ │ │ ├── BP_SampleGDMManager.uasset │ │ │ ├── DA_GameDebugMenu_CameraOnly_ConsoleCommandSet.uasset │ │ │ ├── DA_GameDebugMenu_Sample_ConsoleCommandSet.uasset │ │ │ ├── DA_SampleGDM_Manager.uasset │ │ │ ├── DA_SampleGDM_Master.uasset │ │ │ ├── IA_SampleInput.uasset │ │ │ ├── IMC_Sample.uasset │ │ │ ├── ST_GDM_SampleEnglish.uasset │ │ │ ├── ST_GDM_SampleJapanese.uasset │ │ │ └── WB_GDM_SampleMenu.uasset │ │ ├── E_SampleEnum.uasset │ │ ├── Geometry │ │ │ └── Meshes │ │ │ ├── 1M_Cube.uasset │ │ │ ├── 1M_Cube_Chamfer.uasset │ │ │ ├── CubeMaterial.uasset │ │ │ └── TemplateFloor.uasset │ │ ├── Mannequin │ │ │ ├── Animations │ │ │ │ ├── ThirdPersonIdle.uasset │ │ │ │ ├── ThirdPersonJump_End.uasset │ │ │ │ ├── ThirdPersonJump_Loop.uasset │ │ │ │ ├── ThirdPersonJump_Start.uasset │ │ │ │ ├── ThirdPersonRun.uasset │ │ │ │ ├── ThirdPersonWalk.uasset │ │ │ │ ├── ThirdPerson_AnimBP.uasset │ │ │ │ ├── ThirdPerson_IdleRun_2D.uasset │ │ │ │ └── ThirdPerson_Jump.uasset │ │ │ └── Character │ │ │ ├── Materials │ │ │ │ ├── MI_Female_Body.uasset │ │ │ │ ├── M_Male_Body.uasset │ │ │ │ ├── M_UE4Man_ChestLogo.uasset │ │ │ │ └── MaterialLayers │ │ │ │ ├── ML_GlossyBlack_Latex_UE4.uasset │ │ │ │ ├── ML_Plastic_Shiny_Beige.uasset │ │ │ │ ├── ML_Plastic_Shiny_Beige_LOGO.uasset │ │ │ │ ├── ML_SoftMetal_UE4.uasset │ │ │ │ ├── T_ML_Aluminum01.uasset │ │ │ │ ├── T_ML_Aluminum01_N.uasset │ │ │ │ ├── T_ML_Rubber_Blue_01_D.uasset │ │ │ │ └── T_ML_Rubber_Blue_01_N.uasset │ │ │ ├── Mesh │ │ │ │ ├── SK_Mannequin.uasset │ │ │ │ ├── SK_Mannequin_Female.uasset │ │ │ │ ├── SK_Mannequin_Female_PhysicsAsset.uasset │ │ │ │ ├── SK_Mannequin_PhysicsAsset.uasset │ │ │ │ └── UE4_Mannequin_Skeleton.uasset │ │ │ └── Textures │ │ │ ├── T_Female_Mask.uasset │ │ │ ├── T_Female_N.uasset │ │ │ ├── T_Male_Mask.uasset │ │ │ ├── T_Male_N.uasset │ │ │ ├── T_UE4Logo_Mask.uasset │ │ │ └── T_UE4Logo_N.uasset │ │ ├── ThirdPerson │ │ │ └── Meshes │ │ │ ├── Bump_StaticMesh.uasset │ │ │ ├── LeftArm_StaticMesh.uasset │ │ │ ├── Linear_Stair_StaticMesh.uasset │ │ │ ├── RampMaterial.uasset │ │ │ ├── Ramp_StaticMesh.uasset │ │ │ └── RightArm_StaticMesh.uasset │ │ └── ThirdPersonBP │ │ ├── Blueprints │ │ │ ├── ThirdPersonCharacter.uasset │ │ │ └── ThirdPersonGameMode.uasset │ │ └── Maps │ │ ├── L_LocalPlayTest.umap │ │ └── ThirdPersonExampleMap.umap │ └── StarterContent │ ├── Architecture │ │ ├── Floor_400x400.uasset │ │ ├── Pillar_50x500.uasset │ │ ├── SM_AssetPlatform.uasset │ │ ├── Wall_400x200.uasset │ │ ├── Wall_400x300.uasset │ │ ├── Wall_400x400.uasset │ │ ├── Wall_500x500.uasset │ │ ├── Wall_Door_400x300.uasset │ │ ├── Wall_Door_400x400.uasset │ │ ├── Wall_Window_400x300.uasset │ │ └── Wall_Window_400x400.uasset │ ├── Audio │ │ ├── Collapse01.uasset │ │ ├── Collapse02.uasset │ │ ├── Collapse_Cue.uasset │ │ ├── Explosion01.uasset │ │ ├── Explosion02.uasset │ │ ├── Explosion_Cue.uasset │ │ ├── Fire01.uasset │ │ ├── Fire01_Cue.uasset │ │ ├── Fire_Sparks01.uasset │ │ ├── Fire_Sparks01_Cue.uasset │ │ ├── Light01.uasset │ │ ├── Light01_Cue.uasset │ │ ├── Light02.uasset │ │ ├── Light02_Cue.uasset │ │ ├── Smoke01.uasset │ │ ├── Smoke01_Cue.uasset │ │ ├── Starter_Background_Cue.uasset │ │ ├── Starter_Birds01.uasset │ │ ├── Starter_Music01.uasset │ │ ├── Starter_Music_Cue.uasset │ │ ├── Starter_Wind05.uasset │ │ ├── Starter_Wind06.uasset │ │ ├── Steam01.uasset │ │ └── Steam01_Cue.uasset │ ├── Blueprints │ │ ├── Assets │ │ │ ├── FogBrightnessLUT.uasset │ │ │ ├── M_LightStage_Arrows.uasset │ │ │ ├── M_LightStage_Skybox_Black.uasset │ │ │ ├── M_LightStage_Skybox_HDRI.uasset │ │ │ ├── M_LightStage_Skybox_Master.uasset │ │ │ ├── SM_Arrows.uasset │ │ │ ├── Skybox.uasset │ │ │ └── SunlightColorLUT.uasset │ │ ├── BP_LightStudio.uasset │ │ ├── Blueprint_CeilingLight.uasset │ │ ├── Blueprint_Effect_Explosion.uasset │ │ ├── Blueprint_Effect_Fire.uasset │ │ ├── Blueprint_Effect_Smoke.uasset │ │ ├── Blueprint_Effect_Sparks.uasset │ │ ├── Blueprint_Effect_Steam.uasset │ │ └── Blueprint_WallSconce.uasset │ ├── HDRI │ │ └── HDRI_Epic_Courtyard_Daylight.uasset │ ├── Maps │ │ ├── Advanced_Lighting.umap │ │ ├── Minimal_Default.umap │ │ └── StarterMap.umap │ ├── Materials │ │ ├── M_AssetPlatform.uasset │ │ ├── M_Basic_Floor.uasset │ │ ├── M_Basic_Wall.uasset │ │ ├── M_Brick_Clay_Beveled.uasset │ │ ├── M_Brick_Clay_New.uasset │ │ ├── M_Brick_Clay_Old.uasset │ │ ├── M_Brick_Cut_Stone.uasset │ │ ├── M_Brick_Hewn_Stone.uasset │ │ ├── M_Ceramic_Tile_Checker.uasset │ │ ├── M_CobbleStone_Pebble.uasset │ │ ├── M_CobbleStone_Rough.uasset │ │ ├── M_CobbleStone_Smooth.uasset │ │ ├── M_ColorGrid_LowSpec.uasset │ │ ├── M_Concrete_Grime.uasset │ │ ├── M_Concrete_Panels.uasset │ │ ├── M_Concrete_Poured.uasset │ │ ├── M_Concrete_Tiles.uasset │ │ ├── M_Glass.uasset │ │ ├── M_Ground_Grass.uasset │ │ ├── M_Ground_Gravel.uasset │ │ ├── M_Ground_Moss.uasset │ │ ├── M_Metal_Brushed_Nickel.uasset │ │ ├── M_Metal_Burnished_Steel.uasset │ │ ├── M_Metal_Chrome.uasset │ │ ├── M_Metal_Copper.uasset │ │ ├── M_Metal_Gold.uasset │ │ ├── M_Metal_Rust.uasset │ │ ├── M_Metal_Steel.uasset │ │ ├── M_Rock_Basalt.uasset │ │ ├── M_Rock_Marble_Polished.uasset │ │ ├── M_Rock_Sandstone.uasset │ │ ├── M_Rock_Slate.uasset │ │ ├── M_Tech_Checker_Dot.uasset │ │ ├── M_Tech_Hex_Tile.uasset │ │ ├── M_Tech_Hex_Tile_Pulse.uasset │ │ ├── M_Tech_Panel.uasset │ │ ├── M_Water_Lake.uasset │ │ ├── M_Water_Ocean.uasset │ │ ├── M_Wood_Floor_Walnut_Polished.uasset │ │ ├── M_Wood_Floor_Walnut_Worn.uasset │ │ ├── M_Wood_Oak.uasset │ │ ├── M_Wood_Pine.uasset │ │ └── M_Wood_Walnut.uasset │ ├── Particles │ │ ├── Materials │ │ │ ├── M_Burst.uasset │ │ │ ├── M_Dust_Particle.uasset │ │ │ ├── M_Fire_SubUV.uasset │ │ │ ├── M_Heat_Distortion.uasset │ │ │ ├── M_Radial_Gradient.uasset │ │ │ ├── M_Spark.uasset │ │ │ ├── M_explosion_subUV.uasset │ │ │ ├── M_radial_ramp.uasset │ │ │ ├── M_smoke_subUV.uasset │ │ │ └── m_flare_01.uasset │ │ ├── P_Ambient_Dust.uasset │ │ ├── P_Explosion.uasset │ │ ├── P_Fire.uasset │ │ ├── P_Smoke.uasset │ │ ├── P_Sparks.uasset │ │ └── P_Steam_Lit.uasset │ ├── Props │ │ ├── MaterialSphere.uasset │ │ ├── Materials │ │ │ ├── M_Bush.uasset │ │ │ ├── M_Chair.uasset │ │ │ ├── M_Door.uasset │ │ │ ├── M_Frame.uasset │ │ │ ├── M_Lamp.uasset │ │ │ ├── M_MaterialSphere.uasset │ │ │ ├── M_MaterialSphere_Plain.uasset │ │ │ ├── M_Rock.uasset │ │ │ ├── M_Shelf.uasset │ │ │ ├── M_Statue.uasset │ │ │ ├── M_StatueGlass.uasset │ │ │ └── M_TableRound.uasset │ │ ├── SM_Bush.uasset │ │ ├── SM_Chair.uasset │ │ ├── SM_CornerFrame.uasset │ │ ├── SM_Couch.uasset │ │ ├── SM_Door.uasset │ │ ├── SM_DoorFrame.uasset │ │ ├── SM_GlassWindow.uasset │ │ ├── SM_Lamp_Ceiling.uasset │ │ ├── SM_Lamp_Wall.uasset │ │ ├── SM_MatPreviewMesh_02.uasset │ │ ├── SM_PillarFrame.uasset │ │ ├── SM_PillarFrame300.uasset │ │ ├── SM_Rock.uasset │ │ ├── SM_Shelf.uasset │ │ ├── SM_Stairs.uasset │ │ ├── SM_Statue.uasset │ │ ├── SM_TableRound.uasset │ │ └── SM_WindowFrame.uasset │ ├── Shapes │ │ ├── Shape_Cone.uasset │ │ ├── Shape_Cube.uasset │ │ ├── Shape_Cylinder.uasset │ │ ├── Shape_NarrowCapsule.uasset │ │ ├── Shape_Pipe.uasset │ │ ├── Shape_Pipe_180.uasset │ │ ├── Shape_Pipe_90.uasset │ │ ├── Shape_Plane.uasset │ │ ├── Shape_QuadPyramid.uasset │ │ ├── Shape_Sphere.uasset │ │ ├── Shape_Torus.uasset │ │ ├── Shape_TriPyramid.uasset │ │ ├── Shape_Trim.uasset │ │ ├── Shape_Trim_90_In.uasset │ │ ├── Shape_Trim_90_Out.uasset │ │ ├── Shape_Tube.uasset │ │ ├── Shape_Wedge_A.uasset │ │ ├── Shape_Wedge_B.uasset │ │ └── Shape_WideCapsule.uasset │ └── Textures │ ├── T_Brick_Clay_Beveled_D.uasset │ ├── T_Brick_Clay_Beveled_M.uasset │ ├── T_Brick_Clay_Beveled_N.uasset │ ├── T_Brick_Clay_New_D.uasset │ ├── T_Brick_Clay_New_M.uasset │ ├── T_Brick_Clay_New_N.uasset │ ├── T_Brick_Clay_Old_D.uasset │ ├── T_Brick_Clay_Old_N.uasset │ ├── T_Brick_Cut_Stone_D.uasset │ ├── T_Brick_Cut_Stone_N.uasset │ ├── T_Brick_Hewn_Stone_D.uasset │ ├── T_Brick_Hewn_Stone_M.uasset │ ├── T_Brick_Hewn_Stone_N.uasset │ ├── T_Burst_M.uasset │ ├── T_Bush_D.uasset │ ├── T_Bush_N.uasset │ ├── T_Ceramic_Tile_M.uasset │ ├── T_Ceramic_Tile_N.uasset │ ├── T_Chair_M.uasset │ ├── T_Chair_N.uasset │ ├── T_Checker_Noise_M.uasset │ ├── T_CobbleStone_Pebble_D.uasset │ ├── T_CobbleStone_Pebble_M.uasset │ ├── T_CobbleStone_Pebble_N.uasset │ ├── T_CobbleStone_Rough_D.uasset │ ├── T_CobbleStone_Rough_N.uasset │ ├── T_CobbleStone_Smooth_D.uasset │ ├── T_CobbleStone_Smooth_M.uasset │ ├── T_CobbleStone_Smooth_N.uasset │ ├── T_Concrete_Grime_D.uasset │ ├── T_Concrete_Panels_D.uasset │ ├── T_Concrete_Panels_N.uasset │ ├── T_Concrete_Poured_D.uasset │ ├── T_Concrete_Poured_N.uasset │ ├── T_Concrete_Tiles_D.uasset │ ├── T_Concrete_Tiles_M.uasset │ ├── T_Concrete_Tiles_N.uasset │ ├── T_Concrete_Tiles_Variation_M.uasset │ ├── T_Detail_Rocky_N.uasset │ ├── T_Door_M.uasset │ ├── T_Door_N.uasset │ ├── T_Dust_Particle_D.uasset │ ├── T_Explosion_SubUV.uasset │ ├── T_Fire_SubUV.uasset │ ├── T_Fire_Tiled_D.uasset │ ├── T_Frame_M.uasset │ ├── T_Frame_N.uasset │ ├── T_Gradinet_01.uasset │ ├── T_Ground_Grass_D.uasset │ ├── T_Ground_Grass_N.uasset │ ├── T_Ground_Gravel_D.uasset │ ├── T_Ground_Gravel_N.uasset │ ├── T_Ground_Moss_N.uasset │ ├── T_Lamp_M.uasset │ ├── T_Lamp_N.uasset │ ├── T_MacroVariation.uasset │ ├── T_Metal_Aluminum_D.uasset │ ├── T_Metal_Copper_D.uasset │ ├── T_Metal_Gold_D.uasset │ ├── T_Metal_Gold_N.uasset │ ├── T_Metal_Rust_D.uasset │ ├── T_Metal_Rust_N.uasset │ ├── T_Metal_Steel_D.uasset │ ├── T_Metal_Steel_N.uasset │ ├── T_Perlin_Noise_M.uasset │ ├── T_RockMesh_M.uasset │ ├── T_RockMesh_N.uasset │ ├── T_Rock_Basalt_D.uasset │ ├── T_Rock_Basalt_N.uasset │ ├── T_Rock_Marble_Polished_D.uasset │ ├── T_Rock_Sandstone_D.uasset │ ├── T_Rock_Sandstone_N.uasset │ ├── T_Rock_Slate_D.uasset │ ├── T_Rock_Slate_N.uasset │ ├── T_Rock_Smooth_Granite_D.uasset │ ├── T_Shelf_M.uasset │ ├── T_Shelf_N.uasset │ ├── T_Single_Tile_N.uasset │ ├── T_Smoke_SubUV.uasset │ ├── T_Smoke_Tiled_D.uasset │ ├── T_Spark_Core.uasset │ ├── T_Statue_M.uasset │ ├── T_Statue_N.uasset │ ├── T_TableRound_M.uasset │ ├── T_TableRound_N.uasset │ ├── T_Tech_Dot_M.uasset │ ├── T_Tech_Dot_N.uasset │ ├── T_Tech_Hex_Tile_M.uasset │ ├── T_Tech_Hex_Tile_N.uasset │ ├── T_Tech_Panel_M.uasset │ ├── T_Tech_Panel_N.uasset │ ├── T_Water_M.uasset │ ├── T_Water_N.uasset │ ├── T_Wood_Floor_Walnut_D.uasset │ ├── T_Wood_Floor_Walnut_M.uasset │ ├── T_Wood_Floor_Walnut_N.uasset │ ├── T_Wood_Oak_D.uasset │ ├── T_Wood_Oak_N.uasset │ ├── T_Wood_Pine_D.uasset │ ├── T_Wood_Pine_N.uasset │ ├── T_Wood_Walnut_D.uasset │ ├── T_Wood_Walnut_N.uasset │ └── T_ground_Moss_D.uasset ├── Plugins │ └── GameDebugMenu │ ├── Config │ │ └── DefaultGameDebugMenu.ini │ ├── Content │ │ ├── BPM_GDM_WidgetEvents.uasset │ │ ├── BP_GDM_Manager.uasset │ │ ├── DA_GDM_ConsoleCommandSet.uasset │ │ ├── DA_GDM_Manager.uasset │ │ ├── DA_GDM_Master.uasset │ │ ├── Input │ │ │ ├── BP_GDMDebugCameraInput.uasset │ │ │ ├── IA_GameDebugMenu_Camera_OrbitHitPoint.uasset │ │ │ ├── IA_GameDebugMenu_Camera_PawnTeleport.uasset │ │ │ ├── IA_GameDebugMenu_Cancel.uasset │ │ │ ├── IA_GameDebugMenu_Decide.uasset │ │ │ ├── IA_GameDebugMenu_Down.uasset │ │ │ ├── IA_GameDebugMenu_Favorite.uasset │ │ │ ├── IA_GameDebugMenu_Left.uasset │ │ │ ├── IA_GameDebugMenu_OpenCloseMenu.uasset │ │ │ ├── IA_GameDebugMenu_Right.uasset │ │ │ ├── IA_GameDebugMenu_ShowReport.uasset │ │ │ ├── IA_GameDebugMenu_Up.uasset │ │ │ ├── IMC_GameDebugMenu_Camera.uasset │ │ │ ├── IMC_GameDebugMenu_Manager.uasset │ │ │ └── IMC_GameDebugMenu_Widget.uasset │ │ ├── Misc │ │ │ ├── GDMCurve_Choice.uasset │ │ │ ├── TX_GDM_Close.uasset │ │ │ ├── TX_GDM_Grab.uasset │ │ │ ├── TX_GDM_Pin.uasset │ │ │ └── TX_GDM_Triangle.uasset │ │ ├── StringTable │ │ │ ├── ST_GDM_English.uasset │ │ │ └── ST_GDM_Japanese.uasset │ │ └── UI │ │ ├── Base │ │ │ ├── CategoryMenu │ │ │ │ ├── WB_GDM_CategoryList.uasset │ │ │ │ └── WB_GDM_CategoryMenu.uasset │ │ │ ├── WB_GDM_DebugMenuRoot.uasset │ │ │ └── WB_GDM_RootListButton.uasset │ │ ├── ConsoleCommand │ │ │ ├── WB_GDM_ConsoleCommandBtnBase.uasset │ │ │ ├── WB_GDM_ConsoleCommandBtn_Number.uasset │ │ │ ├── WB_GDM_ConsoleCommandButton.uasset │ │ │ ├── WB_GDM_ConsoleCommandCategoryList.uasset │ │ │ └── WB_GDM_ConsoleCommandMenu.uasset │ │ ├── DebugReport │ │ │ ├── WB_GDM_ReportMenu.uasset │ │ │ └── WB_GDM_ReportTitle.uasset │ │ ├── Favorite │ │ │ ├── BP_GDMFavoriteItemDef_ConsoleCommandBase.uasset │ │ │ ├── BP_GDMFavoriteItemDef_ConsoleCommandGroup.uasset │ │ │ ├── BP_GDMFavoriteItemDef_ConsoleCommandNumber.uasset │ │ │ ├── BP_GDMFavoriteItemDef_ConsoleCommandPair.uasset │ │ │ ├── BP_GDMFavoriteItemDef_ConsoleCommandSingle.uasset │ │ │ ├── BP_GDMFavoriteItemDef_Function.uasset │ │ │ ├── BP_GDMFavoriteItemDef_PropertyBase.uasset │ │ │ ├── BP_GDMFavoriteItemDef_PropertyBool.uasset │ │ │ ├── BP_GDMFavoriteItemDef_PropertyByte.uasset │ │ │ ├── BP_GDMFavoriteItemDef_PropertyEnum.uasset │ │ │ ├── BP_GDMFavoriteItemDef_PropertyFloat.uasset │ │ │ ├── BP_GDMFavoriteItemDef_PropertyInt.uasset │ │ │ ├── BP_GDMFavoriteItemDef_PropertyRotator.uasset │ │ │ ├── BP_GDMFavoriteItemDef_PropertyString.uasset │ │ │ ├── BP_GDMFavoriteItemDef_PropertyVector.uasset │ │ │ ├── BP_GDMFavoriteItemDef_PropertyVector2D.uasset │ │ │ └── WB_GDM_FavoriteMenu.uasset │ │ ├── Gameplay │ │ │ ├── Functions │ │ │ │ └── WB_GDM_FunctionButton.uasset │ │ │ ├── Properties │ │ │ │ ├── ST_GDM_UnusedPropertiesInfo.uasset │ │ │ │ ├── WB_GDM_PropertyBool.uasset │ │ │ │ ├── WB_GDM_PropertyButton.uasset │ │ │ │ ├── WB_GDM_PropertyByte.uasset │ │ │ │ ├── WB_GDM_PropertyEnum.uasset │ │ │ │ ├── WB_GDM_PropertyFloat.uasset │ │ │ │ ├── WB_GDM_PropertyInteger.uasset │ │ │ │ ├── WB_GDM_PropertyRotator.uasset │ │ │ │ ├── WB_GDM_PropertyString.uasset │ │ │ │ ├── WB_GDM_PropertyVector.uasset │ │ │ │ └── WB_GDM_PropertyVector2D.uasset │ │ │ ├── WB_GDM_GameplayCategoryList.uasset │ │ │ └── WB_GDM_GameplayMenu.uasset │ │ ├── GrabViewer │ │ │ ├── GDM_Grab_DragWidget.uasset │ │ │ ├── WB_GDM_GrabViewHandle.uasset │ │ │ └── WB_GDM_GrabViewer.uasset │ │ ├── Interfaces │ │ │ ├── BI_GDM_ButtonChoiceAnimation.uasset │ │ │ ├── BI_GDM_ButtonFavorite.uasset │ │ │ └── BI_GDM_ListWidgetsControlEvent.uasset │ │ ├── Localization │ │ │ ├── WB_GDM_LocalizationButtonBase.uasset │ │ │ ├── WB_GDM_LocalizationButtonCulture.uasset │ │ │ ├── WB_GDM_LocalizationButtonLanguage.uasset │ │ │ ├── WB_GDM_LocalizationButtonLocale.uasset │ │ │ ├── WB_GDM_LocalizationCategoryList.uasset │ │ │ └── WB_GDM_LocalizationMenu.uasset │ │ ├── Parts │ │ │ ├── BP_GDM_PadInputWidgetController.uasset │ │ │ ├── WB_GDM_Button.uasset │ │ │ ├── WB_GDM_CheckBox.uasset │ │ │ ├── WB_GDM_Choice.uasset │ │ │ ├── WB_GDM_ComboBoxString.uasset │ │ │ ├── WB_GDM_FavoritePin.uasset │ │ │ └── WB_GDM_IntSpinBox.uasset │ │ ├── Setting │ │ │ ├── WB_GDM_SettingButtonBase.uasset │ │ │ ├── WB_GDM_SettingButton_DeleteSave.uasset │ │ │ ├── WB_GDM_SettingButton_LanguageKeys.uasset │ │ │ ├── WB_GDM_SettingCategoryList.uasset │ │ │ └── WB_GDM_SettingMenu.uasset │ │ └── WB_GDM_Document.uasset │ ├── GameDebugMenu.uplugin │ ├── LICENSE │ ├── README.md │ ├── Resources │ │ └── Icon128.png │ └── Source │ ├── GameDebugMenu │ │ ├── GameDebugMenu.Build.cs │ │ ├── Private │ │ │ ├── Component │ │ │ │ ├── GDMListenerComponent.cpp │ │ │ │ ├── GDMLocalizeStringComponent.cpp │ │ │ │ ├── GDMPlayerControllerProxyComponent.cpp │ │ │ │ ├── GDMPropertyJsonSystemComponent.cpp │ │ │ │ ├── GDMSaveSystemComponent.cpp │ │ │ │ └── GDMScreenshotRequesterComponent.cpp │ │ │ ├── ConsoleCommand │ │ │ │ ├── GDMConsoleCommandValueProvider.cpp │ │ │ │ ├── GDMConsoleCommandValueProviderComponent.cpp │ │ │ │ ├── GDMConsoleVariableCommandValueProvider.cpp │ │ │ │ └── GDMSlomoCommandValueProvider.cpp │ │ │ ├── Data │ │ │ │ ├── GDMConsoleCommandSetAsset.cpp │ │ │ │ ├── GameDebugMenuManagerAsset.cpp │ │ │ │ └── GameDebugMenuMasterAsset.cpp │ │ │ ├── Favorite │ │ │ │ ├── GDMFavoriteItemDefinition.cpp │ │ │ │ └── GDMFavoriteSystemComponent.cpp │ │ │ ├── GameDebugMenu.cpp │ │ │ ├── GameDebugMenuFunctions.cpp │ │ │ ├── GameDebugMenuManager.cpp │ │ │ ├── GameDebugMenuSettings.cpp │ │ │ ├── GameDebugMenuTypes.cpp │ │ │ ├── Input │ │ │ │ ├── GDMDebugCameraInput.cpp │ │ │ │ ├── GDMEnhancedInputComponent.cpp │ │ │ │ ├── GDMInputSystemComponent.cpp │ │ │ │ ├── GDMInputTriggerPulseWithDelay.cpp │ │ │ │ └── GDMPadInputWidgetController.cpp │ │ │ ├── Log │ │ │ │ └── GDMOutputDevice.cpp │ │ │ ├── Reports │ │ │ │ ├── GDMDebugReportRequester.cpp │ │ │ │ ├── GDMRequesterJira.cpp │ │ │ │ ├── GDMRequesterRedmine.cpp │ │ │ │ └── GDMRequesterTrello.cpp │ │ │ └── Widgets │ │ │ ├── GDMButton.cpp │ │ │ ├── GDMComboBoxString.cpp │ │ │ ├── GDMDebugReportWidget.cpp │ │ │ ├── GDMFunctionWidget.cpp │ │ │ ├── GDMIntSpinBox.cpp │ │ │ ├── GDMPropertyWidget.cpp │ │ │ ├── GDMTextBlock.cpp │ │ │ ├── GameDebugMenuRootWidget.cpp │ │ │ └── GameDebugMenuWidget.cpp │ │ └── Public │ │ ├── Component │ │ │ ├── GDMListenerComponent.h │ │ │ ├── GDMLocalizeStringComponent.h │ │ │ ├── GDMPlayerControllerProxyComponent.h │ │ │ ├── GDMPropertyJsonSystemComponent.h │ │ │ ├── GDMSaveSystemComponent.h │ │ │ └── GDMScreenshotRequesterComponent.h │ │ ├── ConsoleCommand │ │ │ ├── GDMConsoleCommandValueProvider.h │ │ │ ├── GDMConsoleCommandValueProviderComponent.h │ │ │ ├── GDMConsoleVariableCommandValueProvider.h │ │ │ └── GDMSlomoCommandValueProvider.h │ │ ├── Data │ │ │ ├── GDMConsoleCommandSetAsset.h │ │ │ ├── GameDebugMenuManagerAsset.h │ │ │ └── GameDebugMenuMasterAsset.h │ │ ├── Favorite │ │ │ ├── GDMFavoriteItemDefinition.h │ │ │ └── GDMFavoriteSystemComponent.h │ │ ├── GameDebugMenu.h │ │ ├── GameDebugMenuFunctions.h │ │ ├── GameDebugMenuManager.h │ │ ├── GameDebugMenuSettings.h │ │ ├── GameDebugMenuTypes.h │ │ ├── Input │ │ │ ├── GDMDebugCameraInput.h │ │ │ ├── GDMEnhancedInputComponent.h │ │ │ ├── GDMInputSystemComponent.h │ │ │ ├── GDMInputTriggerPulseWithDelay.h │ │ │ └── GDMPadInputWidgetController.h │ │ ├── Log │ │ │ └── GDMOutputDevice.h │ │ ├── Reports │ │ │ ├── GDMDebugReportRequester.h │ │ │ ├── GDMRequesterJira.h │ │ │ ├── GDMRequesterRedmine.h │ │ │ └── GDMRequesterTrello.h │ │ └── Widgets │ │ ├── GDMButton.h │ │ ├── GDMComboBoxString.h │ │ ├── GDMDebugReportWidget.h │ │ ├── GDMFunctionWidget.h │ │ ├── GDMIntSpinBox.h │ │ ├── GDMPropertyWidget.h │ │ ├── GDMTextBlock.h │ │ ├── GameDebugMenuRootWidget.h │ │ └── GameDebugMenuWidget.h │ └── GameDebugMenuEditor │ ├── GameDebugMenuEditor.Build.cs │ ├── Private │ │ ├── AssetTypeActions │ │ │ ├── AssetTypeActions_GDMPlayerControllerProxyComponent.cpp │ │ │ ├── AssetTypeActions_GameDebugMenuManager.cpp │ │ │ └── AssetTypeActions_GameDebugMenuWidget.cpp │ │ ├── DetailCustomizations │ │ │ ├── GDMGameplayCategoryKeyCustomization.cpp │ │ │ └── GDMGameplayCategoryKeyCustomization.h │ │ ├── Factory │ │ │ ├── GDMPlayerControllerProxyComponentFactory.cpp │ │ │ ├── GameDebugMenuManagerFactory.cpp │ │ │ └── GameDebugMenuWidgetFactory.cpp │ │ ├── GameDebugMenuEditor.cpp │ │ └── Pins │ │ ├── GDMGameplayCategoryKeyPin.cpp │ │ ├── GDMGameplayCategoryKeyPin.h │ │ └── GDMGameplayCategoryKeyPinFactory.h │ └── Public │ ├── AssetTypeActions │ │ ├── AssetTypeActions_GDMPlayerControllerProxyComponent.h │ │ ├── AssetTypeActions_GameDebugMenuManager.h │ │ └── AssetTypeActions_GameDebugMenuWidget.h │ ├── Factory │ │ ├── GDMPlayerControllerProxyComponentFactory.h │ │ ├── GameDebugMenuManagerFactory.h │ │ └── GameDebugMenuWidgetFactory.h │ └── GameDebugMenuEditor.h ├── SampleProject.uproject └── Source ├── SampleProject │ ├── MyActor.cpp │ ├── MyActor.h │ ├── SampleProject.Build.cs │ ├── SampleProject.cpp │ └── SampleProject.h ├── SampleProject.Target.cs └── SampleProjectEditor.Target.cs ``` `LICENSE`: ``` MIT License Copyright (c) 2020-2022 000-aki-000 Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ``` `Plugin/GameDebugMenu/Config/DefaultGameDebugMenu.ini`: ```ini [CoreRedirects] +ClassRedirects=(OldName="/Script/GameDebugMenu.GameDebugMenuDataAsset",NewName="/Script/GameDebugMenu.GameDebugMenuManagerAsset") ``` `Plugin/GameDebugMenu/GameDebugMenu.uplugin`: ```uplugin { "FileVersion": 3, "Version": 2, "VersionName": "1.38", "FriendlyName": "GameDebugMenu", "Description": "DebugMenu for UnrealEngine5", "Category": "Debug", "CreatedBy": "akihiko moroi", "CreatedByURL": "https://github.com/000-aki-000/GameDebugMenu", "DocsURL": "", "MarketplaceURL": "", "SupportURL": "", "CanContainContent": true, "IsBetaVersion": false, "Installed": false, "Modules": [ { "Name": "GameDebugMenu", "Type": "Runtime", "LoadingPhase": "Default" }, { "Name": "GameDebugMenuEditor", "Type": "Editor", "LoadingPhase": "PostDefault" } ], "Plugins": [ { "Name": "EnhancedInput", "Enabled": true } ], "IsExperimentalVersion": false } ``` `Plugin/GameDebugMenu/LICENSE`: ``` MIT License Copyright (c) 2020-2022 000-aki-000 Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ``` `Plugin/GameDebugMenu/README.md`: ```md ## License MIT ``` `Plugin/GameDebugMenu/Source/GameDebugMenu/GameDebugMenu.Build.cs`: ```cs /** * Copyright (c) 2020 akihiko moroi * * This software is released under the MIT License. * (See accompanying file LICENSE.txt or copy at http://opensource.org/licenses/MIT) */ using UnrealBuildTool; using System.IO; public class GameDebugMenu : ModuleRules { public GameDebugMenu(ReadOnlyTargetRules Target) : base(Target) { PublicDependencyModuleNames.AddRange( [ "Core", "UMG", "Engine", "InputCore", "Slate", "SlateCore", "HTTP", "Json", "JsonUtilities", "ImageDownload", "ImageWrapper", "EngineSettings", "EnhancedInput" ] ); PrivateDependencyModuleNames.AddRange( [ "CoreUObject", "Engine", "Slate", "SlateCore", "DeveloperSettings", "EnhancedInput" ] ); } } ``` `Plugin/GameDebugMenu/Source/GameDebugMenu/Private/Component/GDMListenerComponent.cpp`: ```cpp /** * Copyright (c) 2020 akihiko moroi * * This software is released under the MIT License. * (See accompanying file LICENSE.txt or copy at http://opensource.org/licenses/MIT) */ #include "Component/GDMListenerComponent.h" #include static TMap>> GlobalListenerComponents; UGDMListenerComponent::UGDMListenerComponent(const FObjectInitializer& ObjectInitializer) : Super(ObjectInitializer) { PrimaryComponentTick.bCanEverTick = false; PrimaryComponentTick.bStartWithTickEnabled = false; bNeverNeedsRenderUpdate = true; bWantsInitializeComponent = true; } void UGDMListenerComponent::InitializeComponent() { Super::InitializeComponent(); PushListenerComponent(GetWorld(),this); } void UGDMListenerComponent::UninitializeComponent() { PopListenerComponent(GetWorld(),this); Super::UninitializeComponent(); } void UGDMListenerComponent::AllUnbindDispatchers() { OnShowDispatcher.Clear(); OnHideDispatcher.Clear(); OnExecuteConsoleCommandDispatcher.Clear(); OnExecuteProcessEventDispatcher.Clear(); OnChangePropertyBoolDispatcher.Clear(); OnChangePropertyIntDispatcher.Clear(); OnChangePropertyFloatDispatcher.Clear(); OnChangePropertyByteDispatcher.Clear(); OnChangePropertyStringDispatcher.Clear(); OnChangePropertyVectorDispatcher.Clear(); OnChangePropertyVector2DDispatcher.Clear(); OnChangePropertyRotatorDispatcher.Clear(); OnChangeDebugMenuLanguageDispatcher.Clear(); OnStartScreenshotRequestDispatcher.Clear(); OnScreenshotRequestProcessedDispatcher.Clear(); OnLoadedDebugMenuDispatcher.Clear(); OnSavedDebugMenuDispatcher.Clear(); OnDeletedDebugMenuDispatcher.Clear(); } int32 UGDMListenerComponent::PushListenerComponent(UWorld* TargetWorld, UGDMListenerComponent* Listener) { TArray>& ListenerComponents = GlobalListenerComponents.FindOrAdd(TargetWorld); return ListenerComponents.AddUnique(Listener); } int32 UGDMListenerComponent::PopListenerComponent(UWorld* TargetWorld, UGDMListenerComponent* Listener) { TArray>& ListenerComponents = GlobalListenerComponents.FindOrAdd(TargetWorld); return ListenerComponents.Remove(Listener); } void UGDMListenerComponent::GetAllListenerComponents(UWorld* TargetWorld, TArray& OutListenerComponents) { if( IsValid(TargetWorld) ) { TArray>& ListenerComponents = GlobalListenerComponents.FindOrAdd(TargetWorld); OutListenerComponents.Reserve(ListenerComponents.Num()); for( int32 Index = ListenerComponents.Num() - 1; Index >= 0; --Index ) { if( ListenerComponents[Index].IsValid() ) { OutListenerComponents.Add(ListenerComponents[Index].Get()); } else { ListenerComponents.RemoveAt(Index); } } if( ListenerComponents.Num() <= 0 ) { GlobalListenerComponents.Remove(TargetWorld); } } } ``` `Plugin/GameDebugMenu/Source/GameDebugMenu/Private/Component/GDMLocalizeStringComponent.cpp`: ```cpp /** * Copyright (c) 2020 akihiko moroi * * This software is released under the MIT License. * (See accompanying file LICENSE.txt or copy at http://opensource.org/licenses/MIT) */ #include "Component/GDMLocalizeStringComponent.h" #include "GameDebugMenuSettings.h" #include "Component/GDMPropertyJsonSystemComponent.h" #include "Internationalization/StringTableCore.h" UGDMLocalizeStringComponent::UGDMLocalizeStringComponent() : CachedDebugMenuStrings() , bCurrentDebugMenuDirectStringKey(false) , CurrentLanguage(NAME_None) { PrimaryComponentTick.bCanEverTick = false; PrimaryComponentTick.bStartWithTickEnabled = false; } void UGDMLocalizeStringComponent::SetJsonSystemComponentValue(UGDMPropertyJsonSystemComponent* PropertyJsonSystemComponent) { FString StringKey = TEXT("DebugMenuDirectStringKey"); { if (!PropertyJsonSystemComponent->HasCustomString(StringKey)) { PropertyJsonSystemComponent->SetCustomString(StringKey, TEXT("False")); } bCurrentDebugMenuDirectStringKey = PropertyJsonSystemComponent->GetCustomString(StringKey, TEXT("False")).ToBool(); } StringKey = TEXT("DebugMenuLanguage"); { if (!PropertyJsonSystemComponent->HasCustomString(StringKey)) { PropertyJsonSystemComponent->SetCustomString(StringKey, GetDefault()->DefaultGameDebugMenuLanguage.ToString()); } CurrentLanguage = *PropertyJsonSystemComponent->GetCustomString(StringKey, GetDefault()->DefaultGameDebugMenuLanguage.ToString()); } } void UGDMLocalizeStringComponent::SetToJsonSystemComponent(UGDMPropertyJsonSystemComponent* PropertyJsonSystemComponent, const FString& Language) { CurrentLanguage = *Language; PropertyJsonSystemComponent->SetCustomString(TEXT("DebugMenuDirectStringKey"), bCurrentDebugMenuDirectStringKey ? TEXT("True") : TEXT("False")); PropertyJsonSystemComponent->SetCustomString(TEXT("DebugMenuLanguage"), Language); } void UGDMLocalizeStringComponent::SyncLoadDebugMenuStringTables() { CachedDebugMenuStrings.Reset(); if( const FGDMStringTableList* StringTableList = GetDefault()->TryGetStringTableList(CurrentLanguage) ) { UE_LOG(LogGDM, Verbose, TEXT("Call SyncLoadDebugMenuStringTables %s"), *CurrentLanguage.ToString()); for( auto& StrTablePtr : StringTableList->StringTables ) { if( !StrTablePtr.ToSoftObjectPath().IsValid() || StrTablePtr.ToSoftObjectPath().IsNull() ) { UE_LOG(LogGDM, Warning, TEXT("SyncLoadDebugMenuStringTables: failed StringTable : LanguageKey->%s"), *CurrentLanguage.ToString()); continue; } if(const UStringTable* StringTable = StrTablePtr.LoadSynchronous() ) { StringTable->GetStringTable()->EnumerateSourceStrings([&](const FString& InKey, const FString& InSourceString) -> bool { if( !CachedDebugMenuStrings.Contains(InKey) ) { CachedDebugMenuStrings.Add(InKey, InSourceString); UE_LOG(LogGDM, VeryVerbose, TEXT("Load string | Key %s SourceString %s"), *InKey, *InSourceString); } else { UE_LOG(LogGDM, Error, TEXT("%s -> StringKey that is already in use!!"), *InKey); } return true; /* すべて取得する */ }); } } } else { UE_LOG(LogGDM, Error, TEXT("Failed Load DebugMenuStringTables %s"), *CurrentLanguage.ToString()); } } ``` `Plugin/GameDebugMenu/Source/GameDebugMenu/Private/Component/GDMPlayerControllerProxyComponent.cpp`: ```cpp /** * Copyright (c) 2020 akihiko moroi * * This software is released under the MIT License. * (See accompanying file LICENSE.txt or copy at http://opensource.org/licenses/MIT) */ #include "Component/GDMPlayerControllerProxyComponent.h" #include #include "GameFramework/PlayerController.h" #include "GameFramework/Character.h" #include "GameDebugMenuFunctions.h" #include "Net/UnrealNetwork.h" UGDMPlayerControllerProxyComponent::UGDMPlayerControllerProxyComponent() { PrimaryComponentTick.bCanEverTick = false; PrimaryComponentTick.bStartWithTickEnabled = false; SetIsReplicatedByDefault(true); } void UGDMPlayerControllerProxyComponent::GetLifetimeReplicatedProps(TArray& OutLifetimeProps ) const { Super::GetLifetimeReplicatedProps( OutLifetimeProps ); DOREPLIFETIME( UGDMPlayerControllerProxyComponent, DebugMenuManager ); } void UGDMPlayerControllerProxyComponent::BeginPlay() { Super::BeginPlay(); UE_LOG(LogGDM, Log, TEXT("Call BeginPlay Spawn GDMPlayerControllerProxyComponent")); } void UGDMPlayerControllerProxyComponent::EndPlay(const EEndPlayReason::Type EndPlayReason) { Super::EndPlay(EndPlayReason); UE_LOG(LogGDM, Log, TEXT("Call EndPlay Destroyed GDMPlayerControllerProxyComponent")); } APlayerController* UGDMPlayerControllerProxyComponent::GetOwnerPlayerController() const { return Cast(GetOwner()); } APawn* UGDMPlayerControllerProxyComponent::GetOwnerPlayerPawn() const { const APlayerController* PlayerController = GetOwnerPlayerController(); if (!IsValid(PlayerController)) { return nullptr; } return PlayerController->GetPawn(); } ACharacter* UGDMPlayerControllerProxyComponent::GetOwnerPlayerCharacter() const { const APlayerController* PlayerController = GetOwnerPlayerController(); if (IsValid(PlayerController)) { return nullptr; } return PlayerController->GetCharacter(); } AGameDebugMenuManager* UGDMPlayerControllerProxyComponent::GetDebugMenuManager() const { return DebugMenuManager; } void UGDMPlayerControllerProxyComponent::ROS_ExecuteConsoleCommand_Implementation(const FString& Command, bool bAllClient) { if (bAllClient) { AllExecuteConsoleCommand_Server(Command); } else { ROC_ExecuteConsoleCommand(Command); } } bool UGDMPlayerControllerProxyComponent::ROS_ExecuteConsoleCommand_Validate(const FString& Command, bool bAllClient) { return (!Command.IsEmpty()); } void UGDMPlayerControllerProxyComponent::ROC_ExecuteConsoleCommand_Implementation(const FString& Command) { GetDebugMenuManager()->ExecuteConsoleCommand(Command, GetOwnerPlayerController()); } bool UGDMPlayerControllerProxyComponent::ROC_ExecuteConsoleCommand_Validate(const FString& Command) { return (!Command.IsEmpty()); } void UGDMPlayerControllerProxyComponent::ExecuteConsoleCommand(const FString& Command, EGDMConsoleCommandNetType CommandNetType) { switch (CommandNetType) { case EGDMConsoleCommandNetType::LocalOnly: { /* 通信せず実行者の環境で実行する */ GetDebugMenuManager()->ExecuteConsoleCommand(Command, GetOwnerPlayerController()); break; } case EGDMConsoleCommandNetType::ServerAll: { /* Serverで全プレイヤーに実行 */ if (GetOwner()->HasAuthority()) { AllExecuteConsoleCommand_Server(Command); } else { ROS_ExecuteConsoleCommand(Command,true); } break; } default: { break; } } } void UGDMPlayerControllerProxyComponent::AllExecuteConsoleCommand_Server(const FString& Command) { for (FConstPlayerControllerIterator Iterator = GetWorld()->GetPlayerControllerIterator(); Iterator; ++Iterator) { APlayerController* PC = Iterator->Get(); if (!IsValid(PC)) { continue; } GetDebugMenuManager()->ExecuteConsoleCommand(Command, PC); } } ``` `Plugin/GameDebugMenu/Source/GameDebugMenu/Private/Component/GDMPropertyJsonSystemComponent.cpp`: ```cpp /** * Copyright (c) 2020 akihiko moroi * * This software is released under the MIT License. * (See accompanying file LICENSE.txt or copy at http://opensource.org/licenses/MIT) */ #include "Component/GDMPropertyJsonSystemComponent.h" #include "GameDebugMenuFunctions.h" #include "Component/GDMListenerComponent.h" const FString UGDMPropertyJsonSystemComponent::JsonField_RootProperty(TEXT("Properties")); const FString UGDMPropertyJsonSystemComponent::JsonField_RootFunction(TEXT("Functions")); const FString UGDMPropertyJsonSystemComponent::JsonField_RootCustom(TEXT("Custom")); const FString UGDMPropertyJsonSystemComponent::JsonField_RootFavorite(TEXT("Favorites")); const FString UGDMPropertyJsonSystemComponent::JsonField_FavoriteDefinitionName(TEXT("DefinitionName")); const FString UGDMPropertyJsonSystemComponent::JsonField_FavoriteSaveKey(TEXT("SaveKey")); UGDMPropertyJsonSystemComponent::UGDMPropertyJsonSystemComponent(const FObjectInitializer& ObjectInitializer) : Super(ObjectInitializer) { RootJsonObject = MakeShared(); } void UGDMPropertyJsonSystemComponent::BeginPlay() { Super::BeginPlay(); /* OwnerはAGameDebugMenuManagerであること前提 */ UGDMListenerComponent* ListenerComp = GetOwner()->GetComponentByClass(); ListenerComp->OnChangePropertyBoolDispatcher.AddUniqueDynamic(this, &UGDMPropertyJsonSystemComponent::OnChangePropertyBool); ListenerComp->OnChangePropertyIntDispatcher.AddUniqueDynamic(this, &UGDMPropertyJsonSystemComponent::OnChangePropertyInt); ListenerComp->OnChangePropertyFloatDispatcher.AddUniqueDynamic(this, &UGDMPropertyJsonSystemComponent::OnChangePropertyFloat); ListenerComp->OnChangePropertyByteDispatcher.AddUniqueDynamic(this, &UGDMPropertyJsonSystemComponent::OnChangePropertyByte); ListenerComp->OnChangePropertyStringDispatcher.AddUniqueDynamic(this, &UGDMPropertyJsonSystemComponent::OnChangePropertyString); ListenerComp->OnChangePropertyVectorDispatcher.AddUniqueDynamic(this, &UGDMPropertyJsonSystemComponent::OnChangePropertyVector); ListenerComp->OnChangePropertyVector2DDispatcher.AddUniqueDynamic(this, &UGDMPropertyJsonSystemComponent::OnChangePropertyVector2D); ListenerComp->OnChangePropertyRotatorDispatcher.AddUniqueDynamic(this, &UGDMPropertyJsonSystemComponent::OnChangePropertyRotator); } void UGDMPropertyJsonSystemComponent::AddPropertyToJson(const FString& ObjectKey, UObject* TargetObject, const FString& PropertyName) const { if (ObjectKey.IsEmpty()) { UE_LOG(LogGDM, Warning, TEXT("AddPropertyToJson: ObjectKey is empty.")); return; } if (!IsValid(TargetObject)) { UE_LOG(LogGDM, Warning, TEXT("AddPropertyToJson: TargetObject is null.")); return; } if (PropertyName.IsEmpty()) { UE_LOG(LogGDM, Warning, TEXT("AddPropertyToJson: PropertyName is empty.")); return; } const FProperty* Property = TargetObject->GetClass()->FindPropertyByName(*PropertyName); if (!Property) { UE_LOG(LogGDM, Warning, TEXT("AddPropertyToJson: Property '%s' not found in object '%s'."), *PropertyName, *TargetObject->GetName()); return; } FString PropertyValue; const void* PropertyValuePtr = Property->ContainerPtrToValuePtr(TargetObject); if (!Property->ExportText_Direct(PropertyValue, PropertyValuePtr, nullptr, TargetObject, PPF_None)) { Property->ExportTextItem_Direct(PropertyValue, PropertyValuePtr, nullptr, TargetObject, PPF_None); } const TSharedPtr* RootPropertyJson = nullptr; if (!RootJsonObject->TryGetObjectField(JsonField_RootProperty, RootPropertyJson)) { /* フィールドが存在しない場合、新しいオブジェクトを作成 */ TSharedPtr NewJsonObject = MakeShareable(new FJsonObject()); RootJsonObject->SetObjectField(JsonField_RootProperty, NewJsonObject); RootPropertyJson = &NewJsonObject; } const TSharedPtr* ObjectJson = nullptr; if (!(*RootPropertyJson)->TryGetObjectField(ObjectKey, ObjectJson)) { /* フィールドが存在しない場合、新しいオブジェクトを作成 */ TSharedPtr NewJsonObject = MakeShareable(new FJsonObject()); (*RootPropertyJson)->SetObjectField(ObjectKey, NewJsonObject); ObjectJson = &NewJsonObject; } (*ObjectJson)->SetStringField(PropertyName, PropertyValue); UE_LOG(LogGDM, Verbose, TEXT("AddPropertyToJson: Added property '%s' with value '%s' to '%s'."), *PropertyName, *PropertyValue, *ObjectKey); } void UGDMPropertyJsonSystemComponent::RemovePropertyFromJson(const FString& ObjectKey, const FString& PropertyName) const { if (ObjectKey.IsEmpty()) { UE_LOG(LogGDM, Warning, TEXT("RemovePropertyFromJson: ObjectKey is empty.")); return; } if (PropertyName.IsEmpty()) { UE_LOG(LogGDM, Warning, TEXT("RemovePropertyFromJson: PropertyName is empty.")); return; } const TSharedPtr* RootPropertyJson = nullptr; if (!RootJsonObject->TryGetObjectField(JsonField_RootProperty, RootPropertyJson)) { UE_LOG(LogGDM, Verbose, TEXT("RemovePropertyFromJson: Property '%s' not found"), *JsonField_RootProperty); return; } const TSharedPtr* ObjectJson = nullptr; if ((*RootPropertyJson)->TryGetObjectField(ObjectKey, ObjectJson)) { if ((*ObjectJson)->HasField(PropertyName)) { (*ObjectJson)->RemoveField(PropertyName); UE_LOG(LogGDM, Verbose, TEXT("RemovePropertyFromJson: Removed property '%s' from '%s'."), *PropertyName, *ObjectKey); } else { UE_LOG(LogGDM, Verbose, TEXT("RemovePropertyFromJson: Property '%s' not found in '%s'."), *PropertyName, *ObjectKey); } } else { UE_LOG(LogGDM, Verbose, TEXT("RemovePropertyFromJson: ObjectKey '%s' not found."), *ObjectKey); } } bool UGDMPropertyJsonSystemComponent::ApplyJsonToObjectProperty(const FString& ObjectKey, UObject* TargetObject, const FString& PropertyName) const { if (ObjectKey.IsEmpty()) { UE_LOG(LogGDM, Warning, TEXT("ApplyJsonToObjectProperty ObjectKey is empty.")); return false; } if (!IsValid(TargetObject)) { UE_LOG(LogGDM, Warning, TEXT("ApplyJsonToObjectProperty TargetObject is nullptr.")); return false; } if (PropertyName.IsEmpty()) { UE_LOG(LogGDM, Warning, TEXT("ApplyJsonToObjectProperty PropertyName is empty.")); return false; } const TSharedPtr* RootPropertyJson = nullptr; if (!RootJsonObject->TryGetObjectField(JsonField_RootProperty, RootPropertyJson)) { UE_LOG(LogGDM, Verbose, TEXT("ApplyJsonToObjectProperty Property '%s' not found"), *JsonField_RootProperty); return false; } const TSharedPtr* ObjectJson = nullptr; if (!(*RootPropertyJson)->TryGetObjectField(ObjectKey, ObjectJson)) { UE_LOG(LogGDM, Verbose, TEXT("ApplyJsonToObjectProperty ObjectKey '%s' not found in JSON."), *ObjectKey); return false; } FString PropertyValue; if (!(*ObjectJson)->TryGetStringField(PropertyName, PropertyValue)) { UE_LOG(LogGDM, Verbose, TEXT("ApplyJsonToObjectProperty Property '%s' not found in JSON for object '%s'."), *PropertyName, *ObjectKey); return false; } const FProperty* Property = TargetObject->GetClass()->FindPropertyByName(*PropertyName); if (Property == nullptr) { UE_LOG(LogGDM, Warning, TEXT("ApplyJsonToObjectProperty Property '%s' not found in target object '%s'."), *PropertyName, *TargetObject->GetName()); return false; } void* PropertyValuePtr = Property->ContainerPtrToValuePtr(TargetObject); if (!Property->ImportText_Direct(*PropertyValue, PropertyValuePtr, nullptr, PPF_None)) { UE_LOG(LogGDM, Verbose, TEXT("ApplyJsonToObjectProperty Failed to set property '%s' with value '%s' for object '%s'."), *PropertyName, *PropertyValue, *ObjectKey); return false; } UE_LOG(LogGDM, Verbose, TEXT("ApplyJsonToObjectProperty Successfully set property '%s' with value '%s' for object '%s'."), *PropertyName, *PropertyValue, *ObjectKey); return true; } void UGDMPropertyJsonSystemComponent::AddFunctionToJson(const FString& ObjectKey, UObject* TargetObject, const FString& FunctionName) const { if (ObjectKey.IsEmpty()) { UE_LOG(LogGDM, Warning, TEXT("AddFunctionToJson: ObjectKey is empty.")); return; } if (!IsValid(TargetObject)) { UE_LOG(LogGDM, Warning, TEXT("AddFunctionToJson: TargetObject is null.")); return; } if (FunctionName.IsEmpty()) { UE_LOG(LogGDM, Warning, TEXT("AddFunctionToJson: FunctionName is empty.")); return; } const UFunction* Function = TargetObject->GetClass()->FindFunctionByName(*FunctionName); if (!IsValid(Function)) { UE_LOG(LogGDM, Warning, TEXT("AddFunctionToJson: Function '%s' not found in object '%s'."), *FunctionName, *TargetObject->GetName()); return; } const TSharedPtr* RootFunctionJson = nullptr; if (!RootJsonObject->TryGetObjectField(JsonField_RootFunction, RootFunctionJson)) { /* フィールドが存在しない場合、新しいオブジェクトを作成 */ TSharedPtr NewJsonObject = MakeShareable(new FJsonObject()); RootJsonObject->SetObjectField(JsonField_RootFunction, NewJsonObject); RootFunctionJson = &NewJsonObject; } const TSharedPtr* ObjectJson = nullptr; if (!(*RootFunctionJson)->TryGetObjectField(ObjectKey, ObjectJson)) { /* フィールドが存在しない場合、新しいオブジェクトを作成 */ TSharedPtr NewJsonObject = MakeShareable(new FJsonObject()); (*RootFunctionJson)->SetObjectField(ObjectKey, NewJsonObject); ObjectJson = &NewJsonObject; } (*ObjectJson)->SetBoolField(FunctionName, true); UE_LOG(LogGDM, Verbose, TEXT("AddFunctionToJson: Added function '%s' with to '%s'."), *FunctionName, *ObjectKey); } void UGDMPropertyJsonSystemComponent::RemoveFunctionFromJson(const FString& ObjectKey, const FString& FunctionName) const { if (ObjectKey.IsEmpty()) { UE_LOG(LogGDM, Warning, TEXT("RemoveFunctionFromJson: ObjectKey is empty.")); return; } if (FunctionName.IsEmpty()) { UE_LOG(LogGDM, Warning, TEXT("RemoveFunctionFromJson: FunctionName is empty.")); return; } const TSharedPtr* RootFunctionJson = nullptr; if (!RootJsonObject->TryGetObjectField(JsonField_RootFunction, RootFunctionJson)) { UE_LOG(LogGDM, Verbose, TEXT("RemoveFunctionFromJson: Function '%s' not found"), *JsonField_RootFunction); return; } const TSharedPtr* ObjectJson = nullptr; if ((*RootFunctionJson)->TryGetObjectField(ObjectKey, ObjectJson)) { if ((*ObjectJson)->HasField(FunctionName)) { (*ObjectJson)->RemoveField(FunctionName); UE_LOG(LogGDM, Verbose, TEXT("RemoveFunctionFromJson: Removed function '%s' from '%s'."), *FunctionName, *ObjectKey); } else { UE_LOG(LogGDM, Verbose, TEXT("RemoveFunctionFromJson: Function '%s' not found in '%s'."), *FunctionName, *ObjectKey); } } else { UE_LOG(LogGDM, Verbose, TEXT("RemoveFunctionFromJson: ObjectKey '%s' not found."), *ObjectKey); } } bool UGDMPropertyJsonSystemComponent::HaveFunctionInJson(const FString& ObjectKey, UObject* TargetObject, const FString& FunctionName) const { if (ObjectKey.IsEmpty()) { UE_LOG(LogGDM, Warning, TEXT("HaveFunctionInJson ObjectKey is empty.")); return false; } if (!IsValid(TargetObject)) { UE_LOG(LogGDM, Warning, TEXT("HaveFunctionInJson TargetObject is nullptr.")); return false; } if (FunctionName.IsEmpty()) { UE_LOG(LogGDM, Warning, TEXT("HaveFunctionInJson FunctionName is empty.")); return false; } const UFunction* Function = TargetObject->GetClass()->FindFunctionByName(*FunctionName); if (!IsValid(Function)) { UE_LOG(LogGDM, Warning, TEXT("HaveFunctionInJson Function '%s' not found in target object '%s'."), *FunctionName, *TargetObject->GetName()); return false; } const TSharedPtr* RootFunctionJson = nullptr; if (!RootJsonObject->TryGetObjectField(JsonField_RootFunction, RootFunctionJson)) { UE_LOG(LogGDM, Verbose, TEXT("HaveFunctionInJson Function '%s' not found"), *JsonField_RootFunction); return false; } const TSharedPtr* ObjectJson = nullptr; if (!(*RootFunctionJson)->TryGetObjectField(ObjectKey, ObjectJson)) { UE_LOG(LogGDM, Verbose, TEXT("HaveFunctionInJson ObjectKey '%s' not found in JSON."), *ObjectKey); return false; } return (*ObjectJson)->HasField(FunctionName); } void UGDMPropertyJsonSystemComponent::AddFavoriteEntry(const FString& DefinitionName, const FString& FavoriteSaveKey) { if (HasFavoriteEntry(DefinitionName, FavoriteSaveKey)) { UE_LOG(LogGDM, Log, TEXT("AddFavoriteEntry: already exists (%s, %s)"), *DefinitionName, *FavoriteSaveKey); return; } TSharedPtr Entry = MakeShared(); Entry->SetStringField(JsonField_FavoriteDefinitionName, DefinitionName); Entry->SetStringField(JsonField_FavoriteSaveKey, FavoriteSaveKey); TArray> Array = RootJsonObject->HasTypedField(JsonField_RootFavorite) ? RootJsonObject->GetArrayField(JsonField_RootFavorite) : TArray>(); Array.Add(MakeShared(Entry)); RootJsonObject->SetArrayField(JsonField_RootFavorite, Array); UE_LOG(LogGDM, Verbose, TEXT("AddFavoriteEntry: DefinitionName %s, FavoriteSaveKey %s "), *DefinitionName, *FavoriteSaveKey); } bool UGDMPropertyJsonSystemComponent::RemoveFavoriteEntry(const FString& DefinitionName, const FString& FavoriteSaveKey) { if (!RootJsonObject->HasTypedField(JsonField_RootFavorite)) { return false; } TArray> Array = RootJsonObject->GetArrayField(JsonField_RootFavorite); const int32 OriginalCount = Array.Num(); Array.RemoveAll([&](const TSharedPtr& Value) { const TSharedPtr* ObjPtr = nullptr; if (Value->TryGetObject(ObjPtr)) { return (*ObjPtr)->GetStringField(JsonField_FavoriteDefinitionName) == DefinitionName && (*ObjPtr)->GetStringField(JsonField_FavoriteSaveKey) == FavoriteSaveKey; } return false; }); if (Array.Num() != OriginalCount) { /* 減ったら再セット */ RootJsonObject->SetArrayField(JsonField_RootFavorite, Array); UE_LOG(LogGDM, Verbose, TEXT("RemoveFavoriteEntry: DefinitionName %s, FavoriteSaveKey %s '%d'->'%d'"), *DefinitionName, *FavoriteSaveKey, OriginalCount, Array.Num()); return true; } return false; } bool UGDMPropertyJsonSystemComponent::HasFavoriteEntry(const FString& DefinitionName, const FString& FavoriteSaveKey) const { if (!RootJsonObject->HasTypedField(JsonField_RootFavorite)) { return false; } const TArray> Array = RootJsonObject->GetArrayField(JsonField_RootFavorite); for (const TSharedPtr& Value : Array) { const TSharedPtr* ObjPtr = nullptr; if (Value->TryGetObject(ObjPtr)) { if ((*ObjPtr)->GetStringField(JsonField_FavoriteDefinitionName) == DefinitionName && (*ObjPtr)->GetStringField(JsonField_FavoriteSaveKey) == FavoriteSaveKey) { return true; } } } return false; } TArray UGDMPropertyJsonSystemComponent::GetAllFavoriteEntries() const { TArray OutEntries; if (!RootJsonObject->HasTypedField(JsonField_RootFavorite)) { return OutEntries; } const TArray> JsonArray = RootJsonObject->GetArrayField(JsonField_RootFavorite); for (const TSharedPtr& Value : JsonArray) { const TSharedPtr* ObjPtr = nullptr; if (Value->TryGetObject(ObjPtr)) { FGDMFavoriteEntry Entry; Entry.DefinitionName = (*ObjPtr)->GetStringField(JsonField_FavoriteDefinitionName); Entry.SaveKey = (*ObjPtr)->GetStringField(JsonField_FavoriteSaveKey); OutEntries.Add(Entry); } } return OutEntries; } void UGDMPropertyJsonSystemComponent::SetCustomStringArray(const FString& Key, const TArray& StringArray) { if (Key.IsEmpty()) { UE_LOG(LogGDM, Warning, TEXT("SetCustomStringArray: Key is empty.")); return; } TSharedPtr RootCustomJson; if (RootJsonObject->HasTypedField(JsonField_RootCustom)) { RootCustomJson = RootJsonObject->GetObjectField(JsonField_RootCustom); } else { RootCustomJson = MakeShared(); RootJsonObject->SetObjectField(JsonField_RootCustom, RootCustomJson); } TArray> JsonArray; for (const FString& Value : StringArray) { JsonArray.Add(MakeShared(Value)); } RootCustomJson->SetArrayField(Key, JsonArray); UE_LOG(LogGDM, Verbose, TEXT("SetCustomStringArray: Added array under key '%s'"), *Key); } void UGDMPropertyJsonSystemComponent::SetCustomString(const FString& Key, const FString& StringValue) { if (Key.IsEmpty()) { UE_LOG(LogGDM, Warning, TEXT("SetCustomString: Key is empty.")); return; } TSharedPtr RootCustomJson; if (RootJsonObject->HasTypedField(JsonField_RootCustom)) { RootCustomJson = RootJsonObject->GetObjectField(JsonField_RootCustom); } else { RootCustomJson = MakeShared(); RootJsonObject->SetObjectField(JsonField_RootCustom, RootCustomJson); } RootCustomJson->SetStringField(Key, StringValue); UE_LOG(LogGDM, Verbose, TEXT("SetCustomString: Set '%s' to key '%s'."), *StringValue, *Key); } TArray UGDMPropertyJsonSystemComponent::GetCustomStringArray(const FString& Key) const { TArray Result; if (!HasCustomString(Key)) { return Result; } const TSharedPtr RootCustomJson = RootJsonObject->GetObjectField(JsonField_RootCustom); const TArray>* JsonArray = nullptr; if (RootCustomJson->TryGetArrayField(Key, JsonArray)) { for (const TSharedPtr& Value : *JsonArray) { if (Value->Type == EJson::String) { Result.Add(Value->AsString()); UE_LOG(LogGDM, Verbose, TEXT("GetCustomStringArray: Add string value '%s' '%s'."), *Key, *Value->AsString()); } else { UE_LOG(LogGDM, Warning, TEXT("GetCustomStringArray: Non-string value found in array for key '%s'."), *Key); } } } return Result; } FString UGDMPropertyJsonSystemComponent::GetCustomString(const FString& Key, const FString& DefaultValue) const { if (HasCustomString(Key)) { return RootJsonObject->GetObjectField(JsonField_RootCustom)->GetStringField(Key); } return DefaultValue; } bool UGDMPropertyJsonSystemComponent::HasCustomString(const FString& Key) const { if (Key.IsEmpty()) { UE_LOG(LogGDM, Warning, TEXT("HasCustomString: Key is empty.")); return false; } if (!RootJsonObject->HasTypedField(JsonField_RootCustom)) { return false; } return RootJsonObject->GetObjectField(JsonField_RootCustom)->HasField(Key); } FString UGDMPropertyJsonSystemComponent::GetJsonAsString() const { FString JsonString; const TSharedRef> Writer = TJsonWriterFactory<>::Create(&JsonString); if (FJsonSerializer::Serialize(RootJsonObject.ToSharedRef(), Writer)) { UE_LOG(LogGDM, Verbose, TEXT("GetJsonAsString: %s"), *JsonString); return JsonString; } return TEXT(""); } bool UGDMPropertyJsonSystemComponent::BuildJsonFromString(const FString& JsonString) { if (JsonString.IsEmpty()) { UE_LOG(LogGDM, Warning, TEXT("BuildJsonFromString: Input JSON string is empty.")); return false; } TSharedPtr ParsedJsonObject; TSharedRef> Reader = TJsonReaderFactory<>::Create(JsonString); if (!FJsonSerializer::Deserialize(Reader, ParsedJsonObject) || !ParsedJsonObject.IsValid()) { UE_LOG(LogGDM, Error, TEXT("BuildJsonFromString: Failed to parse JSON string.")); return false; } RootJsonObject = ParsedJsonObject; UE_LOG(LogGDM, Verbose, TEXT("BuildJsonFromString: Successfully updated RootJsonObject.")); return true; } void UGDMPropertyJsonSystemComponent::OnChangePropertyBool(const FName& PropertyName, UObject* PropertyOwnerObject, bool New, bool Old, const FString& PropertySaveKey) { if (!PropertySaveKey.IsEmpty()) { AddPropertyToJson(PropertySaveKey, PropertyOwnerObject, PropertyName.ToString()); } } void UGDMPropertyJsonSystemComponent::OnChangePropertyInt(const FName& PropertyName, UObject* PropertyOwnerObject, int32 New, int32 Old, const FString& PropertySaveKey) { if (!PropertySaveKey.IsEmpty()) { AddPropertyToJson(PropertySaveKey, PropertyOwnerObject, PropertyName.ToString()); } } void UGDMPropertyJsonSystemComponent::OnChangePropertyFloat(const FName& PropertyName, UObject* PropertyOwnerObject, float New, float Old, const FString& PropertySaveKey) { if (!PropertySaveKey.IsEmpty()) { AddPropertyToJson(PropertySaveKey, PropertyOwnerObject, PropertyName.ToString()); } } void UGDMPropertyJsonSystemComponent::OnChangePropertyByte(const FName& PropertyName, UObject* PropertyOwnerObject, uint8 New, uint8 Old, const FString& PropertySaveKey) { if (!PropertySaveKey.IsEmpty()) { AddPropertyToJson(PropertySaveKey, PropertyOwnerObject, PropertyName.ToString()); } } void UGDMPropertyJsonSystemComponent::OnChangePropertyString(const FName& PropertyName, UObject* PropertyOwnerObject, FString New, FString Old, const FString& PropertySaveKey) { if (!PropertySaveKey.IsEmpty()) { AddPropertyToJson(PropertySaveKey, PropertyOwnerObject, PropertyName.ToString()); } } void UGDMPropertyJsonSystemComponent::OnChangePropertyVector(const FName& PropertyName, UObject* PropertyOwnerObject, FVector New, FVector Old, const FString& PropertySaveKey) { if (!PropertySaveKey.IsEmpty()) { AddPropertyToJson(PropertySaveKey, PropertyOwnerObject, PropertyName.ToString()); } } void UGDMPropertyJsonSystemComponent::OnChangePropertyVector2D(const FName& PropertyName, UObject* PropertyOwnerObject, FVector2D New, FVector2D Old, const FString& PropertySaveKey) { if (!PropertySaveKey.IsEmpty()) { AddPropertyToJson(PropertySaveKey, PropertyOwnerObject, PropertyName.ToString()); } } void UGDMPropertyJsonSystemComponent::OnChangePropertyRotator(const FName& PropertyName, UObject* PropertyOwnerObject, FRotator New, FRotator Old, const FString& PropertySaveKey) { if (!PropertySaveKey.IsEmpty()) { AddPropertyToJson(PropertySaveKey, PropertyOwnerObject, PropertyName.ToString()); } } ``` `Plugin/GameDebugMenu/Source/GameDebugMenu/Private/Component/GDMSaveSystemComponent.cpp`: ```cpp /** * Copyright (c) 2020 akihiko moroi * * This software is released under the MIT License. * (See accompanying file LICENSE.txt or copy at http://opensource.org/licenses/MIT) */ #include "Component/GDMSaveSystemComponent.h" #include "GameDebugMenuManager.h" #include "GameDebugMenuSettings.h" #include "Component/GDMPropertyJsonSystemComponent.h" #include "Kismet/GameplayStatics.h" UGDMSaveSystemComponent::UGDMSaveSystemComponent(const FObjectInitializer& ObjectInitializer) : Super(ObjectInitializer) , UserIndex(0) , SaveGame(nullptr) { PrimaryComponentTick.bCanEverTick = false; PrimaryComponentTick.bStartWithTickEnabled = false; } void UGDMSaveSystemComponent::SaveDebugMenuFile() { if (GetDefault()->bDisableSaveFile) { return; } AGameDebugMenuManager* Manager = Cast(GetOwner()); if (!IsValid(Manager)) { UE_LOG(LogGDM, Error, TEXT("SaveDebugMenuFile: GameDebugMenuManager not found on owner actor.")); return; } UGDMPropertyJsonSystemComponent* JsonSystemComponent = GetPropertyJsonSystemComponent(); if (!IsValid(JsonSystemComponent)) { UE_LOG(LogGDM, Error, TEXT("SaveDebugMenuFile: PropertyJsonSystemComponent not found on the same actor.")); return; } TArray CommandHistory; if (!GetDefault()->bDoesNotSaveConsoleCommand) { Manager->GetOutputCommandHistoryString(CommandHistory); } JsonSystemComponent->SetCustomStringArray(TEXT("CommandHistory"), CommandHistory); const FString JsonString = JsonSystemComponent->GetJsonAsString(); if (JsonString.IsEmpty()) { UE_LOG(LogGDM, Log, TEXT("SaveDebugMenuFile: JsonString is empty.")); return; } if (SaveFile(JsonString)) { Manager->CallSavedDebugMenuDispatcher(); } } void UGDMSaveSystemComponent::LoadDebugMenuFile() { if (GetDefault()->bDisableSaveFile) { return; } AGameDebugMenuManager* Manager = Cast(GetOwner()); if (!IsValid(Manager)) { UE_LOG(LogGDM, Error, TEXT("LoadDebugMenuFile: GameDebugMenuManager not found on owner actor.")); return; } UGDMPropertyJsonSystemComponent* JsonSystemComponent = GetPropertyJsonSystemComponent(); if (!IsValid(JsonSystemComponent)) { UE_LOG(LogGDM, Error, TEXT("LoadDebugMenuFile: JsonSystemComponent not found on the same actor.")); return; } FString LoadedJsonString; if (!LoadFile(LoadedJsonString)) { return; } if (!JsonSystemComponent->BuildJsonFromString(LoadedJsonString)) { UE_LOG(LogGDM, Warning, TEXT("LoadDebugMenuFile: Failed to apply JSON to JsonSystemComponent.")); /* ファイルがないため現状の状態を1度保存する */ SaveDebugMenuFile(); return; } Manager->CallLoadedDebugMenuDispatcher(); UE_LOG(LogGDM, Log, TEXT("LoadDebugMenuFile: JSON loaded and applied to JsonSystemComponent.")); } void UGDMSaveSystemComponent::DeleteDebugMenuFile() { AGameDebugMenuManager* Manager = Cast(GetOwner()); if (!IsValid(Manager)) { UE_LOG(LogGDM, Error, TEXT("DeleteDebugMenuFile: GameDebugMenuManager not found on owner actor.")); return; } if (!DeleteFile()) { return; } Manager->CallDeletedDebugMenuDispatcher(); UE_LOG(LogGDM, Log, TEXT("DeleteDebugMenuFile: JSON deleted and applied to JsonSystemComponent.")); } UGDMPropertyJsonSystemComponent* UGDMSaveSystemComponent::GetPropertyJsonSystemComponent() const { if (const AActor* Owner = GetOwner()) { return Owner->FindComponentByClass(); } UE_LOG(LogGDM, Warning, TEXT("GetPropertyJsonSystemComponent: Owner is null or component not found.")); return nullptr; } bool UGDMSaveSystemComponent::SaveFile(const FString& ContentString) { if (CanUseSaveGame()) { if (!IsValid(SaveGame)) { SaveGame = Cast(UGameplayStatics::CreateSaveGameObject(UGDMSaveGame::StaticClass())); } SaveGame->Json = ContentString; const FString SlotName = GetDefault()->SaveFileName; UGameplayStatics::SaveGameToSlot(SaveGame, SlotName, UserIndex); UE_LOG(LogGDM, Log, TEXT("SaveFile: JSON saved to SlotName '%s' UserIndex '%d'"), *SlotName, UserIndex); return true; } else { const FString SaveFilePath = GetDefault()->GetFullSavePath(); if (FFileHelper::SaveStringToFile(ContentString, *SaveFilePath)) { UE_LOG(LogGDM, Log, TEXT("SaveFile: JSON saved to '%s'"), *SaveFilePath); return true; } UE_LOG(LogGDM, Verbose, TEXT("SaveFile: Failed to save JSON to '%s'"), *SaveFilePath); } return false; } bool UGDMSaveSystemComponent::LoadFile(FString& OutLoadedContentString) { OutLoadedContentString.Reset(); if (CanUseSaveGame()) { const FString SlotName = GetDefault()->SaveFileName; SaveGame = Cast(UGameplayStatics::LoadGameFromSlot(SlotName, UserIndex)); if (!IsValid(SaveGame)) { SaveGame = Cast(UGameplayStatics::CreateSaveGameObject(UGDMSaveGame::StaticClass())); } else { UE_LOG(LogGDM, Log, TEXT("LoadFile: JSON loaded to SlotName '%s' UserIndex '%d'"), *SlotName, UserIndex); } OutLoadedContentString = SaveGame->Json; return true; } else { const FString LoadFilePath = GetDefault()->GetFullSavePath(); if (FFileHelper::LoadFileToString(OutLoadedContentString, *LoadFilePath)) { UE_LOG(LogGDM, Log, TEXT("LoadFile: JSON loaded to '%s'"), *LoadFilePath); return true; } UE_LOG(LogGDM, Verbose, TEXT("LoadFile: Failed to load JSON to '%s'"), *LoadFilePath); } return false; } bool UGDMSaveSystemComponent::DeleteFile() { if (CanUseSaveGame()) { const FString SlotName = GetDefault()->SaveFileName; if (UGameplayStatics::DeleteGameInSlot(SlotName, UserIndex)) { return true; } UE_LOG(LogGDM, Warning, TEXT("DeleteFile: Failed to DeleteGameInSlot to '%s'"), *SlotName); } else { const FString FilePath = GetDefault()->GetFullSavePath(); if (FFileHelper::SaveStringToFile(FString(), *FilePath))/* ファイル削除がないので空で上書き */ { UE_LOG(LogGDM, Log, TEXT("DeleteFile: JSON saved to '%s'"), *FilePath); return true; } UE_LOG(LogGDM, Error, TEXT("DeleteFile: Failed to save JSON to '%s'"), *FilePath); } return false; } bool UGDMSaveSystemComponent::CanUseSaveGame() { if (!GetDefault()->bUseSaveGame) { /* テキストファイルで直接書き込み */ #if (PLATFORM_WINDOWS || PLATFORM_MAC) /* 他のプラットフォームだとテキストが扱えないかもしれないため PC のみ */ return false; #endif } /* データはSaveGameを使用 */ return true; } ``` `Plugin/GameDebugMenu/Source/GameDebugMenu/Private/Component/GDMScreenshotRequesterComponent.cpp`: ```cpp /** * Copyright (c) 2020 akihiko moroi * * This software is released under the MIT License. * (See accompanying file LICENSE.txt or copy at http://opensource.org/licenses/MIT) */ #include "Component/GDMScreenshotRequesterComponent.h" #include "Engine/GameViewportClient.h" #include "UnrealClient.h" #include "TimerManager.h" #include "GameDebugMenuManager.h" #include "GameDebugMenuFunctions.h" #include "Widgets/GDMDebugReportWidget.h" UGDMScreenshotRequesterComponent::UGDMScreenshotRequesterComponent(const FObjectInitializer& ObjectInitializer) : Super(ObjectInitializer) , OnScreenshotCapturedHandle() , OnScreenshotRequestProcessedHandle() , bRequestProcessed(true) { PrimaryComponentTick.bCanEverTick = false; PrimaryComponentTick.bStartWithTickEnabled = false; } void UGDMScreenshotRequesterComponent::BeginPlay() { Super::BeginPlay(); } void UGDMScreenshotRequesterComponent::EndPlay(const EEndPlayReason::Type EndPlayReason) { Super::EndPlay(EndPlayReason); ResetHandle(); } void UGDMScreenshotRequesterComponent::RequestScreenshot() { ResetHandle(); bRequestProcessed = false; OnScreenshotCapturedHandle = UGameViewportClient::OnScreenshotCaptured().AddUObject(this, &UGDMScreenshotRequesterComponent::OnScreenshotCaptured); OnScreenshotRequestProcessedHandle = FScreenshotRequest::OnScreenshotRequestProcessed().AddUObject(this, &UGDMScreenshotRequesterComponent::OnScreenshotRequestProcessed); GetOwnerGameDebugMenuManager()->CallStartScreenshotRequestDispatcher(); /* ↑実際のキャプチャ処理は1フレーム後 */ GetWorld()->GetTimerManager().SetTimerForNextTick(FTimerDelegate::CreateLambda([this]() { /* スクショ開始 */ FScreenshotRequest::Reset(); FScreenshotRequest::RequestScreenshot(true); })); } bool UGDMScreenshotRequesterComponent::IsRequestProcessed() { return bRequestProcessed; } AGameDebugMenuManager* UGDMScreenshotRequesterComponent::GetOwnerGameDebugMenuManager() const { return Cast(GetOwner()); } void UGDMScreenshotRequesterComponent::OnScreenshotCaptured(int32 Width, int32 Height, const TArray& Bitmap) { UGameDebugMenuFunctions::PrintLogScreen(this, TEXT("OnScreenshotCaptured"), 4.0f); if(AGameDebugMenuManager* DebugMenuManager = GetOwnerGameDebugMenuManager()) { TArray Widgets; DebugMenuManager->GetDebugMenuWidgetInstances(Widgets); for(const auto Widget : Widgets) { /* Debugレポート用Widgetがあれば通知してあげる */ if(UGDMDebugReportWidget* ReportWidget = Cast(Widget)) { ReportWidget->OnScreenshotCaptured(Width, Height, Bitmap); } } } } void UGDMScreenshotRequesterComponent::OnScreenshotRequestProcessed() { ResetHandle(); UGameDebugMenuFunctions::PrintLogScreen(this, TEXT("OnScreenshotRequestProcessed"), 4.0f); bRequestProcessed = true; GetOwnerGameDebugMenuManager()->CallScreenshotRequestProcessedDispatcher(); } void UGDMScreenshotRequesterComponent::ResetHandle() { UGameViewportClient::OnScreenshotCaptured().Remove(OnScreenshotCapturedHandle); FScreenshotRequest::OnScreenshotRequestProcessed().Remove(OnScreenshotRequestProcessedHandle); OnScreenshotCapturedHandle.Reset(); OnScreenshotRequestProcessedHandle.Reset(); } ``` `Plugin/GameDebugMenu/Source/GameDebugMenu/Private/ConsoleCommand/GDMConsoleCommandValueProvider.cpp`: ```cpp /** * Copyright (c) 2020 akihiko moroi * * This software is released under the MIT License. * (See accompanying file LICENSE.txt or copy at http://opensource.org/licenses/MIT) */ #include "ConsoleCommand/GDMConsoleCommandValueProvider.h" ``` `Plugin/GameDebugMenu/Source/GameDebugMenu/Private/ConsoleCommand/GDMConsoleCommandValueProviderComponent.cpp`: ```cpp /** * Copyright (c) 2020 akihiko moroi * * This software is released under the MIT License. * (See accompanying file LICENSE.txt or copy at http://opensource.org/licenses/MIT) */ #include "ConsoleCommand/GDMConsoleCommandValueProviderComponent.h" #include "ConsoleCommand/GDMConsoleVariableCommandValueProvider.h" UGDMConsoleCommandValueProviderComponent::UGDMConsoleCommandValueProviderComponent() { PrimaryComponentTick.bCanEverTick = false; PrimaryComponentTick.bStartWithTickEnabled = false; } bool UGDMConsoleCommandValueProviderComponent::GetFloatValue(const FString& CommandName, float& OutValue) { for (const FGDMConsoleCommandProviderPattern& PatternStruct : ProviderPatterns) { if (PatternStruct.Pattern.IsEmpty() || !IsValid(PatternStruct.Provider)) { continue; } if (CommandName.Contains(PatternStruct.Pattern)) { return PatternStruct.Provider->GetFloatValue(CommandName, OutValue); } } if (IsValid(ConsoleVariableCommandValueProvider)) { return ConsoleVariableCommandValueProvider->GetFloatValue(CommandName, OutValue); } return false; } ``` `Plugin/GameDebugMenu/Source/GameDebugMenu/Private/ConsoleCommand/GDMConsoleVariableCommandValueProvider.cpp`: ```cpp /** * Copyright (c) 2020 akihiko moroi * * This software is released under the MIT License. * (See accompanying file LICENSE.txt or copy at http://opensource.org/licenses/MIT) */ #include "ConsoleCommand/GDMConsoleVariableCommandValueProvider.h" bool UGDMConsoleVariableCommandValueProvider::GetFloatValue_Implementation(const FString& CommandName, float& OutValue) const { if (const IConsoleVariable* Var = IConsoleManager::Get().FindConsoleVariable(*CommandName)) { OutValue = Var->GetFloat(); return true; } return false; } ``` `Plugin/GameDebugMenu/Source/GameDebugMenu/Private/ConsoleCommand/GDMSlomoCommandValueProvider.cpp`: ```cpp /** * Copyright (c) 2020 akihiko moroi * * This software is released under the MIT License. * (See accompanying file LICENSE.txt or copy at http://opensource.org/licenses/MIT) */ #include "ConsoleCommand/GDMSlomoCommandValueProvider.h" bool UGDMSlomoCommandValueProvider::GetFloatValue_Implementation(const FString& CommandName, float& OutValue) const { if (const UWorld* World = GetWorld()) { if (IsValid(World->GetWorldSettings())) { OutValue = World->GetWorldSettings()->GetEffectiveTimeDilation(); return true; } } return false; } ``` `Plugin/GameDebugMenu/Source/GameDebugMenu/Private/Data/GDMConsoleCommandSetAsset.cpp`: ```cpp /** * Copyright (c) 2020 akihiko moroi * * This software is released under the MIT License. * (See accompanying file LICENSE.txt or copy at http://opensource.org/licenses/MIT) */ #include "Data/GDMConsoleCommandSetAsset.h" #include "GameDebugMenuSettings.h" void UGDMConsoleCommandSetAsset::SetupCommandNames() { #if WITH_EDITORONLY_DATA const UGameDebugMenuSettings* Settings = GetDefault(); auto FilterByCategory = [this](const auto& CommandArray, auto& OutArray) { OutArray.Reset(); if (CategoryIndexList.IsEmpty()) { for (const auto& Item : CommandArray) { OutArray.Add(Item); } } else { for (const auto& Item : CommandArray) { if (CategoryIndexList.Contains(Item.CategoryIndex)) { OutArray.Add(Item); } } } }; FilterByCategory(Settings->ConsoleCommandNames, ConsoleCommandNames); FilterByCategory(Settings->ConsoleCommandGroups, ConsoleCommandGroups); FilterByCategory(Settings->ConsoleCommandPairs, ConsoleCommandPairs); FilterByCategory(Settings->ConsoleCommandNumbers, ConsoleCommandNumbers); FilterByCategory(Settings->EditorOnlyConsoleCommandNames, EditorOnlyConsoleCommandNames); FilterByCategory(Settings->EditorOnlyConsoleCommandGroups, EditorOnlyConsoleCommandGroups); FilterByCategory(Settings->EditorOnlyConsoleCommandPairs, EditorOnlyConsoleCommandPairs); FilterByCategory(Settings->EditorOnlyConsoleCommandNumbers, EditorOnlyConsoleCommandNumbers); this->Modify(); #endif } void UGDMConsoleCommandSetAsset::SetupOrderConsoleCommandCategoryTitles() { #if WITH_EDITORONLY_DATA const UGameDebugMenuSettings* Settings = GetDefault(); OrderConsoleCommandCategoryTitles = Settings->OrderConsoleCommandCategoryTitles; this->Modify(); #endif } void UGDMConsoleCommandSetAsset::MergeFromSourceAssets() { #if WITH_EDITORONLY_DATA auto MergeCommands = [](auto& Target, const auto& Source) { for (const auto& Item : Source) { const FString NewId = Item.BuildCommandIdentifier(); if (!Target.ContainsByPredicate([&](const auto& Existing) { return Existing.BuildCommandIdentifier() == NewId; })) { Target.Add(Item); } } }; for (const UGDMConsoleCommandSetAsset* SourceAsset : MergeSourceAssets) { if (!IsValid(SourceAsset)) { continue; } MergeCommands(ConsoleCommandNames, SourceAsset->ConsoleCommandNames); MergeCommands(ConsoleCommandGroups, SourceAsset->ConsoleCommandGroups); MergeCommands(ConsoleCommandPairs, SourceAsset->ConsoleCommandPairs); MergeCommands(ConsoleCommandNumbers, SourceAsset->ConsoleCommandNumbers); MergeCommands(EditorOnlyConsoleCommandNames, SourceAsset->EditorOnlyConsoleCommandNames); MergeCommands(EditorOnlyConsoleCommandGroups, SourceAsset->EditorOnlyConsoleCommandGroups); MergeCommands(EditorOnlyConsoleCommandPairs, SourceAsset->EditorOnlyConsoleCommandPairs); MergeCommands(EditorOnlyConsoleCommandNumbers, SourceAsset->EditorOnlyConsoleCommandNumbers); } this->Modify(); #endif } TArray UGDMConsoleCommandSetAsset::GetOrderConsoleCommandCategoryTitle() const { TArray ReturnValues; const auto& TitleArray = OrderConsoleCommandCategoryTitles; for( int32 Index = 0;Index < TitleArray.Num();++Index ) { ReturnValues.Add(FGDMMenuCategoryKey(TitleArray[Index].Index, TitleArray[Index].Title)); } return ReturnValues; } bool UGDMConsoleCommandSetAsset::GetConsoleCommandNameByArrayIndex(const int32 ArrayIndex, FGDMConsoleCommandSingle& Out) const { #if WITH_EDITOR TArray Commands; Commands.Append(ConsoleCommandNames); Commands.Append(EditorOnlyConsoleCommandNames); if (!Commands.IsValidIndex(ArrayIndex)) { return false; } Out = Commands[ArrayIndex]; #else const auto& Commands = ConsoleCommandNames; if (!Commands.IsValidIndex(ArrayIndex)) { return false; } Out = Commands[ArrayIndex]; #endif return true; } int32 UGDMConsoleCommandSetAsset::GetNumConsoleCommandNames() const { #if WITH_EDITOR TArray Commands; Commands.Append(ConsoleCommandNames); Commands.Append(EditorOnlyConsoleCommandNames); return Commands.Num(); #else return ConsoleCommandNames.Num(); #endif } bool UGDMConsoleCommandSetAsset::GetConsoleCommandGroupByArrayIndex(const int32 ArrayIndex, FGDMConsoleCommandGroup& Out) const { #if WITH_EDITOR TArray Commands; Commands.Append(ConsoleCommandGroups); Commands.Append(EditorOnlyConsoleCommandGroups); if (!Commands.IsValidIndex(ArrayIndex)) { return false; } Out = Commands[ArrayIndex]; #else const auto& Commands = ConsoleCommandGroups; if (!Commands.IsValidIndex(ArrayIndex)) { return false; } Out = Commands[ArrayIndex]; #endif return true; } int32 UGDMConsoleCommandSetAsset::GetNumConsoleCommandGroups() const { #if WITH_EDITOR TArray Commands; Commands.Append(ConsoleCommandGroups); Commands.Append(EditorOnlyConsoleCommandGroups); return Commands.Num(); #else return ConsoleCommandGroups.Num(); #endif } bool UGDMConsoleCommandSetAsset::GetConsoleCommandPairByArrayIndex(const int32 ArrayIndex, FGDMConsoleCommandPair& Out) const { #if WITH_EDITOR TArray Commands; Commands.Append(ConsoleCommandPairs); Commands.Append(EditorOnlyConsoleCommandPairs); if (!Commands.IsValidIndex(ArrayIndex)) { return false; } Out = Commands[ArrayIndex]; #else const auto& Commands = ConsoleCommandPairs; if (!Commands.IsValidIndex(ArrayIndex)) { return false; } Out = Commands[ArrayIndex]; #endif return true; } int32 UGDMConsoleCommandSetAsset::GetNumConsoleCommandPairs() const { #if WITH_EDITOR TArray Commands; Commands.Append(ConsoleCommandPairs); Commands.Append(EditorOnlyConsoleCommandPairs); return Commands.Num(); #else return ConsoleCommandPairs.Num(); #endif } bool UGDMConsoleCommandSetAsset::GetConsoleCommandNumberByArrayIndex(const int32 ArrayIndex, FGDMConsoleCommandNumber& Out) const { #if WITH_EDITOR TArray Commands; Commands.Append(ConsoleCommandNumbers); Commands.Append(EditorOnlyConsoleCommandNumbers); if (!Commands.IsValidIndex(ArrayIndex)) { return false; } Out = Commands[ArrayIndex]; #else const auto& Commands = ConsoleCommandNumbers; if (!Commands.IsValidIndex(ArrayIndex)) { return false; } Out = Commands[ArrayIndex]; #endif return true; } int32 UGDMConsoleCommandSetAsset::GetNumConsoleCommandNumbers() const { #if WITH_EDITOR TArray Commands; Commands.Append(ConsoleCommandNumbers); Commands.Append(EditorOnlyConsoleCommandNumbers); return Commands.Num(); #else return ConsoleCommandNumbers.Num(); #endif } bool UGDMConsoleCommandSetAsset::FindConsoleCommandSingleById(const FString& CommandId, FGDMConsoleCommandSingle& Out) const { for (const FGDMConsoleCommandSingle& Cmd : ConsoleCommandNames) { if (Cmd.BuildCommandIdentifier() == CommandId) { Out = Cmd; return true; } } #if WITH_EDITOR for (const FGDMConsoleCommandSingle& Cmd : EditorOnlyConsoleCommandNames) { if (Cmd.BuildCommandIdentifier() == CommandId) { Out = Cmd; return true; } } #endif return false; } bool UGDMConsoleCommandSetAsset::FindConsoleCommandGroupById(const FString& CommandId, FGDMConsoleCommandGroup& Out) const { for (const FGDMConsoleCommandGroup& Cmd : ConsoleCommandGroups) { if (Cmd.BuildCommandIdentifier() == CommandId) { Out = Cmd; return true; } } #if WITH_EDITOR for (const FGDMConsoleCommandGroup& Cmd : EditorOnlyConsoleCommandGroups) { if (Cmd.BuildCommandIdentifier() == CommandId) { Out = Cmd; return true; } } #endif return false; } bool UGDMConsoleCommandSetAsset::FindConsoleCommandPairById(const FString& CommandId, FGDMConsoleCommandPair& Out) const { for (const FGDMConsoleCommandPair& Cmd : ConsoleCommandPairs) { if (Cmd.BuildCommandIdentifier() == CommandId) { Out = Cmd; return true; } } #if WITH_EDITOR for (const FGDMConsoleCommandPair& Cmd : EditorOnlyConsoleCommandPairs) { if (Cmd.BuildCommandIdentifier() == CommandId) { Out = Cmd; return true; } } #endif return false; } bool UGDMConsoleCommandSetAsset::FindConsoleCommandNumberById(const FString& CommandId, FGDMConsoleCommandNumber& Out) const { for (const FGDMConsoleCommandNumber& Cmd : ConsoleCommandNumbers) { if (Cmd.BuildCommandIdentifier() == CommandId) { Out = Cmd; return true; } } #if WITH_EDITOR for (const FGDMConsoleCommandNumber& Cmd : EditorOnlyConsoleCommandNumbers) { if (Cmd.BuildCommandIdentifier() == CommandId) { Out = Cmd; return true; } } #endif return false; } ``` `Plugin/GameDebugMenu/Source/GameDebugMenu/Private/Data/GameDebugMenuManagerAsset.cpp`: ```cpp /** * Copyright (c) 2020 akihiko moroi * * This software is released under the MIT License. * (See accompanying file LICENSE.txt or copy at http://opensource.org/licenses/MIT) */ #include "Data/GameDebugMenuManagerAsset.h" #include "Component/GDMPlayerControllerProxyComponent.h" #include "Input/GDMDebugCameraInput.h" UGameDebugMenuManagerAsset::UGameDebugMenuManagerAsset() : Super() , DebugMenuRootWidgetClass() , DebugMenuClasses() , DebugMenuRegistrationOrder() , RootWidgetZOrder(TNumericLimits::Max() - 100)/* モバイルとかの仮想パッドより上になるよう強制で10加算されるので問題内容100小さくしてる。UGameViewportSubsystem::AddToScreen参照 */ , AddInputMappingContextWhenCreateManager() , AddInputMappingContextWhenDebugMenuIsShow() , DebugCameraInputClass(nullptr) , FavoriteItemDefinitions() , DebugMenuPCProxyComponentClass() , bGamePause(false) { DebugMenuPCProxyComponentClass = UGDMPlayerControllerProxyComponent::StaticClass(); DebugCameraInputClass = AGDMDebugCameraInput::StaticClass(); } ``` `Plugin/GameDebugMenu/Source/GameDebugMenu/Private/Data/GameDebugMenuMasterAsset.cpp`: ```cpp /** * Copyright (c) 2020 akihiko moroi * * This software is released under the MIT License. * (See accompanying file LICENSE.txt or copy at http://opensource.org/licenses/MIT) */ #include "Data/GameDebugMenuMasterAsset.h" UGameDebugMenuMasterAsset::UGameDebugMenuMasterAsset(const FObjectInitializer& ObjectInitializer) : Super(ObjectInitializer) , DebugMenuManagerClasses() , DebugMenuInputComponentClass(nullptr) , DebugReportRequesterClass() , GameDebugMenuStringTables() , FontName(nullptr) { } FPrimaryAssetId UGameDebugMenuMasterAsset::GetPrimaryAssetId() const { return FPrimaryAssetId(GetPrimaryType(), GetFName()); } const FPrimaryAssetType& UGameDebugMenuMasterAsset::GetPrimaryType() { static const FPrimaryAssetType AssetType = TEXT("GameDebugMenuMaster"); return AssetType; } TSoftClassPtr UGameDebugMenuMasterAsset::GetGameDebugMenuManagerSoftClass(FString ClassName) const { for (const TSoftClassPtr& ClassPtr : DebugMenuManagerClasses) { if (ClassPtr.IsNull()) { continue; } FString AssetName = ClassPtr.GetAssetName(); AssetName.RemoveFromEnd(TEXT("_C")); if (AssetName == ClassName) { return ClassPtr; } } return nullptr; } ``` `Plugin/GameDebugMenu/Source/GameDebugMenu/Private/Favorite/GDMFavoriteItemDefinition.cpp`: ```cpp /** * Copyright (c) 2020 akihiko moroi * * This software is released under the MIT License. * (See accompanying file LICENSE.txt or copy at http://opensource.org/licenses/MIT) */ #include "Favorite/GDMFavoriteItemDefinition.h" #include "GameDebugMenuManager.h" UWorld* UGDMFavoriteItemDefinition::GetWorld() const { if (UPackage* PkgObj = Cast(GetOuter())) { return GWorld; } if (OwnerManager.IsValid()) { return OwnerManager.Get()->GetWorld(); } return nullptr; } AGameDebugMenuManager* UGDMFavoriteItemDefinition::GetOwnerManager() const { if (OwnerManager.IsValid()) { return OwnerManager.Get(); } return nullptr; } ``` `Plugin/GameDebugMenu/Source/GameDebugMenu/Private/Favorite/GDMFavoriteSystemComponent.cpp`: ```cpp /** * Copyright (c) 2020 akihiko moroi * * This software is released under the MIT License. * (See accompanying file LICENSE.txt or copy at http://opensource.org/licenses/MIT) */ #include "Favorite/GDMFavoriteSystemComponent.h" #include "GameDebugMenuManager.h" #include "GameDebugMenuTypes.h" #include "Blueprint/UserWidget.h" #include "Component/GDMPropertyJsonSystemComponent.h" #include "Data/GameDebugMenuManagerAsset.h" #include "Favorite/GDMFavoriteItemDefinition.h" UGDMFavoriteSystemComponent::UGDMFavoriteSystemComponent() { PrimaryComponentTick.bCanEverTick = false; PrimaryComponentTick.bStartWithTickEnabled = false; } UGDMPropertyJsonSystemComponent* UGDMFavoriteSystemComponent::GetPropertyJsonSystemComponent() const { if (const AActor* Owner = GetOwner()) { return Owner->FindComponentByClass(); } UE_LOG(LogGDM, Warning, TEXT("GetPropertyJsonSystemComponent: Owner is null or component not found.")); return nullptr; } void UGDMFavoriteSystemComponent::Initialize(const UGameDebugMenuManagerAsset* InMenuAsset) { if (!IsValid(InMenuAsset)) { UE_LOG(LogGDM, Warning, TEXT("UGDMFavoriteSystemComponent::Initialize: Invalid MenuDataAsset.")); return; } CachedFavoriteDefinitions = InMenuAsset->FavoriteItemDefinitions; for (auto& Def : CachedFavoriteDefinitions) { Def->OwnerManager = Cast(GetOwner()); } } bool UGDMFavoriteSystemComponent::ToggleAddOrRemoveFavorite(UUserWidget* TargetWidget) { if (CanFavorite(TargetWidget)) { if (IsFavorited(TargetWidget)) { RemoveFavorite(TargetWidget); } else { AddFavorite(TargetWidget); } return true; } return false; } void UGDMFavoriteSystemComponent::AddFavorite(UUserWidget* TargetWidget) { if (!IsValid(TargetWidget)) { UE_LOG(LogGDM, Warning, TEXT("AddFavorite: Invalid target or MenuDataAsset.")); return; } UGDMPropertyJsonSystemComponent* JsonSystem = GetPropertyJsonSystemComponent(); if (!IsValid(JsonSystem)) { UE_LOG(LogGDM, Warning, TEXT("AddFavorite: JsonSystem not found.")); return; } for (const auto& Def : CachedFavoriteDefinitions) { if (!IsValid(Def) || !Def->FavoriteWidgetClass) { continue; } if (!TargetWidget->IsA(Def->FavoriteWidgetClass)) { continue; } const FString SaveKey = Def->ExportToFavoriteKey(TargetWidget); if (SaveKey.IsEmpty()) { UE_LOG(LogGDM, Warning, TEXT("AddFavorite: ExportToFavoriteKey failed or returned empty.")); return; } const FString DefinitionName = Def->GetClass()->GetName(); if (JsonSystem->HasFavoriteEntry(DefinitionName, SaveKey)) { UE_LOG(LogGDM, Log, TEXT("AddFavorite: Already exists (%s, %s)"), *DefinitionName, *SaveKey); return; } JsonSystem->AddFavoriteEntry(DefinitionName, SaveKey); UE_LOG(LogGDM, Verbose, TEXT("AddFavorite: Added (%s, %s)"), *DefinitionName, *SaveKey); return; } UE_LOG(LogGDM, Warning, TEXT("AddFavorite: No matching FavoriteItemDefinition found for widget class %s"), *GetNameSafe(TargetWidget->GetClass())); } void UGDMFavoriteSystemComponent::RemoveFavorite(UUserWidget* TargetWidget) { if (!IsValid(TargetWidget)) { return; } UGDMPropertyJsonSystemComponent* JsonSystem = GetPropertyJsonSystemComponent(); if (!IsValid(JsonSystem)) { return; } for (const auto& Def : CachedFavoriteDefinitions) { if (!IsValid(Def) || !TargetWidget->IsA(Def->FavoriteWidgetClass)) { continue; } const FString SaveKey = Def->ExportToFavoriteKey(TargetWidget); if (SaveKey.IsEmpty()) { continue; } const FString DefinitionName = Def->GetClass()->GetName(); JsonSystem->RemoveFavoriteEntry(DefinitionName, SaveKey); return; } } bool UGDMFavoriteSystemComponent::CanFavorite(UUserWidget* TargetWidget) { if (!IsValid(TargetWidget)) { UE_LOG(LogGDM, Warning, TEXT("AddFavorite: TargetWidget is invalid.")); return false; } for (const auto& Def : CachedFavoriteDefinitions) { if (!IsValid(Def) || !Def->FavoriteWidgetClass) { continue; } if (!TargetWidget->IsA(Def->FavoriteWidgetClass)) { continue; } if (!Def->IsSupportedWidget(TargetWidget)) { continue; } /* お気に入り指定可能 */ return true; } return false; } bool UGDMFavoriteSystemComponent::IsFavorited(UUserWidget* TargetWidget) { if (!IsValid(TargetWidget)) { return false; } UGDMPropertyJsonSystemComponent* JsonSystem = GetPropertyJsonSystemComponent(); if (!IsValid(JsonSystem)) { return false; } for (const auto& Def : CachedFavoriteDefinitions) { if (!IsValid(Def) || !TargetWidget->IsA(Def->FavoriteWidgetClass)) { continue; } const FString SaveKey = Def->ExportToFavoriteKey(TargetWidget); if (SaveKey.IsEmpty()) { continue; } const FString DefinitionName = Def->GetClass()->GetName(); if (JsonSystem->HasFavoriteEntry(DefinitionName, SaveKey)) { return true; } } return false; } void UGDMFavoriteSystemComponent::ClearAllFavorites() { if (UGDMPropertyJsonSystemComponent* JsonSystem = GetPropertyJsonSystemComponent()) { const TArray All = JsonSystem->GetAllFavoriteEntries(); for (const FGDMFavoriteEntry& Entry : All) { JsonSystem->RemoveFavoriteEntry(Entry.DefinitionName, Entry.SaveKey); } } } bool UGDMFavoriteSystemComponent::IsFavoriteListEqualTo(const TArray& OtherList) const { if (const UGDMPropertyJsonSystemComponent* JsonSystem = GetPropertyJsonSystemComponent()) { const TArray Current = JsonSystem->GetAllFavoriteEntries(); if (Current.Num() != OtherList.Num()) { /* 件数が異なるのでNG */ return false; } for (int32 i = 0; i < Current.Num(); ++i) { if (Current[i].DefinitionName != OtherList[i].DefinitionName || Current[i].SaveKey != OtherList[i].SaveKey) { /* いずれかのデータが合わないのでNG */ return false; } } return true; } /* JSONが取得できなければ常に差異ありとみなす */ return false; } UUserWidget* UGDMFavoriteSystemComponent::CreateFavoriteWidgetFromEntry(const FGDMFavoriteEntry& Entry) { if (UGDMPropertyJsonSystemComponent* JsonSystem = GetPropertyJsonSystemComponent()) { for (UGDMFavoriteItemDefinition* Def : CachedFavoriteDefinitions) { if (IsValid(Def) && Def->GetClass()->GetName() == Entry.DefinitionName) { return Def->CreateWidgetFromFavoriteData(Entry.SaveKey); } } } UE_LOG(LogGDM, Warning, TEXT("CreateFavoriteWidgetFromEntry: Definition not found: %s"), *Entry.DefinitionName); return nullptr; } ``` `Plugin/GameDebugMenu/Source/GameDebugMenu/Private/GameDebugMenu.cpp`: ```cpp /** * Copyright (c) 2020 akihiko moroi * * This software is released under the MIT License. * (See accompanying file LICENSE.txt or copy at http://opensource.org/licenses/MIT) */ #include "GameDebugMenu.h" #define LOCTEXT_NAMESPACE "FGameDebugMenuModule" void FGameDebugMenuModule::StartupModule() { UE_LOG(LogTemp, Display, TEXT("FGameDebugMenuModule StartupModule")); } void FGameDebugMenuModule::ShutdownModule() { UE_LOG(LogTemp, Display, TEXT("FGameDebugMenuModule ShutdownModule")); } #undef LOCTEXT_NAMESPACE IMPLEMENT_MODULE(FGameDebugMenuModule, GameDebugMenu) ``` `Plugin/GameDebugMenu/Source/GameDebugMenu/Private/GameDebugMenuFunctions.cpp`: ```cpp /** * Copyright (c) 2020 akihiko moroi * * This software is released under the MIT License. * (See accompanying file LICENSE.txt or copy at http://opensource.org/licenses/MIT) */ #include "GameDebugMenuFunctions.h" #include "GameFramework/WorldSettings.h" #include "Engine/Engine.h" #include "Kismet/GameplayStatics.h" #include "CoreGlobals.h" #include "Misc/ConfigCacheIni.h" #include #include "GameDebugMenuSettings.h" #include "Component/GDMLocalizeStringComponent.h" #include "Data/GameDebugMenuMasterAsset.h" #include "Input/GDMInputSystemComponent.h" TArray< TWeakObjectPtr > GGameDebugMenuManagers; TArray UGameDebugMenuFunctions::RegisterPendingProperties; TArray UGameDebugMenuFunctions::RegisterPendingFunctions; FDelegateHandle UGameDebugMenuFunctions::ActorSpawnedDelegateHandle; bool UGameDebugMenuFunctions::bDisableGameDebugMenu = false; UGameDebugMenuFunctions::UGameDebugMenuFunctions(const FObjectInitializer& ObjectInitializer) : Super(ObjectInitializer) { if(!IsRunningCommandlet()) { auto& ConsoleManager = IConsoleManager::Get(); const FString ShowCommand = TEXT("GDM.Show"); const FString HideCommand = TEXT("GDM.Hide"); const FString ToggleMenuCommand = TEXT("GDM.ToggleMenu"); const FString ToggleInputCommand = TEXT("GDM.ToggleInput"); if(ConsoleManager.FindConsoleObject(*ShowCommand) == nullptr) { ConsoleManager.RegisterConsoleCommand(*ShowCommand, TEXT("Show Game Debug Menu"), FConsoleCommandDelegate::CreateStatic(UGameDebugMenuFunctions::ShowDebugConsoleCommand), ECVF_Default); } if(ConsoleManager.FindConsoleObject(*HideCommand) == nullptr) { ConsoleManager.RegisterConsoleCommand(*HideCommand, TEXT("Hide Game Debug Menu"), FConsoleCommandDelegate::CreateStatic(UGameDebugMenuFunctions::HideDebugConsoleCommand), ECVF_Default); } if(ConsoleManager.FindConsoleObject(*ToggleMenuCommand) == nullptr) { ConsoleManager.RegisterConsoleCommand(*ToggleMenuCommand, TEXT("Toggle Game Debug Menu"), FConsoleCommandDelegate::CreateStatic(UGameDebugMenuFunctions::ToggleDebugConsoleCommand), ECVF_Default); } if( ConsoleManager.FindConsoleObject(*ToggleInputCommand) == nullptr ) { ConsoleManager.RegisterConsoleCommand(*ToggleInputCommand, TEXT("Toggle Game Debug Menu InputSystem Log"), FConsoleCommandDelegate::CreateStatic(UGameDebugMenuFunctions::ToggleInputSystemLog), ECVF_Default); } } } void UGameDebugMenuFunctions::RegisterGameDebugMenuManagerInstance(AGameDebugMenuManager* RegisterManager) { GGameDebugMenuManagers.AddUnique(RegisterManager); if( !UKismetSystemLibrary::IsServer(RegisterManager) ) { /* Client */ OnActorSpawnedClientWaitManager(RegisterManager); } } void UGameDebugMenuFunctions::UnregisterGameDebugMenuManagerInstance(AGameDebugMenuManager* UnregisterManager) { GGameDebugMenuManagers.Remove(UnregisterManager); } bool UGameDebugMenuFunctions::TryCreateDebugMenuManager(APlayerController* PlayerController, FString DebugMenuManagerClassName) { bool bServer = UKismetSystemLibrary::IsServer(PlayerController); FString NetMode = (bServer ? TEXT("Server") : TEXT("Client")); UWorld* World = GEngine->GetWorldFromContextObject(PlayerController, EGetWorldErrorMode::LogAndReturnNull); if(World == nullptr) { UE_LOG(LogGDM, Warning, TEXT("TryCreateDebugMenuManager: Not found world[%s]"), *NetMode); return false; } if (DebugMenuManagerClassName.IsEmpty()) { UE_LOG(LogGDM, Warning, TEXT("TryCreateDebugMenuManager: class name empty[%s]"), *NetMode); return false; } const UGameDebugMenuMasterAsset* MasterAsset = GetDefault()->GetMasterAsset(); if (!IsValid(MasterAsset)) { UE_LOG(LogGDM, Warning, TEXT("TryCreateDebugMenuManager: Not found MasterAsset[%s]"), *NetMode); /* 生成ができない環境なので無効フラグを立てる */ bDisableGameDebugMenu = true; RegisterPendingProperties.Empty(); RegisterPendingFunctions.Empty(); return false; } const auto ManagerClass = MasterAsset->GetGameDebugMenuManagerSoftClass(DebugMenuManagerClassName); UClass* DebugMenuManagerClass = ManagerClass.LoadSynchronous(); if(DebugMenuManagerClass == nullptr) { FString ErrorStr = TEXT("DebugMenuManagerClass NotFound LoadError"); ErrorStr += TEXT("["); ErrorStr += NetMode; ErrorStr += TEXT("]"); UE_LOG(LogGDM, Warning, TEXT("TryCreateDebugMenuManager: Failed Class name [%s] %s"), *NetMode, *DebugMenuManagerClassName); UKismetSystemLibrary::PrintString(PlayerController, ErrorStr, true, true, FLinearColor::Red, 10.0f); /* 生成ができない環境なので無効フラグを立てる */ bDisableGameDebugMenu = true; RegisterPendingProperties.Empty(); RegisterPendingFunctions.Empty(); return false; } bDisableGameDebugMenu = false; if(!bServer) { /* Client */ if( !IsValid(GetGameDebugMenuManager(PlayerController, false)) ) { /* まだ生成してもらってないので待つ */ UE_LOG(LogGDM, Log, TEXT("TryCreateDebugMenuManager: Wait spawn[%s]"), *NetMode); return true; } } else { /* Server */ if (IsValid(TryGetGameDebugMenuManagerFromPlayerController(PlayerController))) { UE_LOG(LogGDM, Log, TEXT("TryCreateDebugMenuManager: Created manager[%s]"), *NetMode); return true; } FActorSpawnParameters SpawnParams; SpawnParams.Owner = PlayerController; SpawnParams.SpawnCollisionHandlingOverride = ESpawnActorCollisionHandlingMethod::AlwaysSpawn; AGameDebugMenuManager* Manager = World->SpawnActor(DebugMenuManagerClass, FTransform::Identity, SpawnParams); for (FConstPlayerControllerIterator Iterator = World->GetPlayerControllerIterator(); Iterator; ++Iterator) { APlayerController* PC = Iterator->Get(); if (!IsValid(PC)) { continue; } /* PlayerControllerにコンポーネントを追加 */ Manager->AddDebugMenuPCProxyComponent(PC); } ActorSpawnedDelegateHandle = World->AddOnActorSpawnedHandler(FOnActorSpawned::FDelegate::CreateStatic(&UGameDebugMenuFunctions::OnActorSpawnedServer)); } /* マネージャー生成前に登録処理したプロパティ群を追加する */ for(int32 Index = 0; Index < RegisterPendingProperties.Num(); ++Index) { if(RegisterPendingProperties.IsValidIndex(Index)) { const auto& PendingData = RegisterPendingProperties[Index]; if(PendingData.TargetObject.IsValid()) { RegisterGDMObjectProperty(PendingData.TargetObject.Get() ,PendingData.ConfigInfo ,PendingData.TargetName ,PendingData.CategoryKey ,PendingData.SaveKey ,PendingData.DisplayPropertyName ,PendingData.Description ,PendingData.DisplayPriority ); } } } /* マネージャー生成前に登録処理した関数群を追加する */ for(int32 Index = 0; Index < RegisterPendingFunctions.Num(); ++Index) { if(RegisterPendingFunctions.IsValidIndex(Index)) { const auto& PendingData = RegisterPendingFunctions[Index]; if(PendingData.TargetObject.IsValid()) { RegisterGDMObjectFunction(PendingData.TargetObject.Get(), PendingData.TargetName, PendingData.CategoryKey, PendingData.SaveKey, PendingData.DisplayPropertyName, PendingData.Description, PendingData.DisplayPriority ); } } } RegisterPendingProperties.RemoveAll([&](const FGDMPendingObjectData& PropertyData) { if(!PropertyData.TargetObject.IsValid()) { return true; } return (PropertyData.TargetObject->GetWorld() == World ); }); RegisterPendingFunctions.RemoveAll([&](const FGDMPendingObjectData& FunctionData) { if(!FunctionData.TargetObject.IsValid()) { return true; } return (FunctionData.TargetObject->GetWorld() == World ); }); UE_LOG(LogGDM, Log, TEXT("TryCreateDebugMenuManager: Success %s"), *NetMode); return true; } bool UGameDebugMenuFunctions::DestroyDebugMenuManager(APlayerController* PlayerController) { UWorld* World = GEngine->GetWorldFromContextObject(PlayerController, EGetWorldErrorMode::LogAndReturnNull); if(World != nullptr) { World->RemoveOnActorSpawnedHandler(ActorSpawnedDelegateHandle); RegisterPendingProperties.RemoveAll([&](const FGDMPendingObjectData& PropertyData) { if (!PropertyData.TargetObject.IsValid()) { return true; } return (PropertyData.TargetObject->GetWorld() == World); }); RegisterPendingFunctions.RemoveAll([&](const FGDMPendingObjectData& FunctionData) { if (!FunctionData.TargetObject.IsValid()) { return true; } return (FunctionData.TargetObject->GetWorld() == World); }); } AGameDebugMenuManager* GDMManager = GetGameDebugMenuManager(PlayerController, false); if(IsValid(GDMManager)) { GDMManager->HideDebugMenu(); GDMManager->Destroy(); return true; } return false; } AGameDebugMenuManager* UGameDebugMenuFunctions::GetGameDebugMenuManager(const UObject* WorldContextObject, const bool bCheckInitialize) { if(bDisableGameDebugMenu) { return nullptr; } const UWorld* World = GEngine->GetWorldFromContextObject(WorldContextObject, EGetWorldErrorMode::LogAndReturnNull); if( !IsValid(World) ) { UE_LOG(LogGDM, Warning, TEXT("GetGameDebugMenuManager: not found World")); return nullptr; } AGameDebugMenuManager* GDMManager = nullptr; for (const auto Manager : GGameDebugMenuManagers ) { if (Manager->GetWorld() == World) { if (IsValid(Manager->GetOwner())) { GDMManager = Cast(Manager); break; } } } if (IsValid(GDMManager)) { if(bCheckInitialize) { return GDMManager->IsInitializedManager() ? GDMManager : nullptr; } return GDMManager; } return nullptr; } AGameDebugMenuManager* UGameDebugMenuFunctions::TryGetGameDebugMenuManagerFromPlayerController(const APlayerController* PlayerController) { for (const auto Manager : GGameDebugMenuManagers ) { if (Manager->GetOwner() == PlayerController) { return Cast(Manager); } } return nullptr; } bool UGameDebugMenuFunctions::ShowDebugMenu(UObject* WorldContextObject) { if(bDisableGameDebugMenu) { UE_LOG(LogGDM, Warning, TEXT("ShowDebugMenu: GameDebugMenu is disabled")); return false; } AGameDebugMenuManager* GDMManager = GetGameDebugMenuManager(WorldContextObject); if(!IsValid(GDMManager)) { UE_LOG(LogGDM, Warning, TEXT("ShowDebugMenu: Instance Not Found")); return false; } return GDMManager->ShowDebugMenu(); } bool UGameDebugMenuFunctions::HideDebugMenu(UObject* WorldContextObject) { AGameDebugMenuManager* GDMManager = GetGameDebugMenuManager(WorldContextObject); if(!IsValid(GDMManager)) { UE_LOG(LogGDM, Log, TEXT("HideDebugMenu: Instance Not Found")); return false; } GDMManager->HideDebugMenu(); return true; } bool UGameDebugMenuFunctions::ToggleDebugMenu(UObject* WorldContextObject) { AGameDebugMenuManager* GDMManager = GetGameDebugMenuManager(WorldContextObject); if(!IsValid(GDMManager)) { UE_LOG(LogGDM, Log, TEXT("ToggleDebugMenu: Instance Not Found")); return false; } if(GDMManager->IsShowingDebugMenu()) { GDMManager->HideDebugMenu(); } else { GDMManager->ShowDebugMenu(); } return true; } bool UGameDebugMenuFunctions::IsShowingDebugMenu(UObject* WorldContextObject) { AGameDebugMenuManager* GDMManager = GetGameDebugMenuManager(WorldContextObject); if(!IsValid(GDMManager)) { return false; } return GDMManager->IsShowingDebugMenu(); } UGameDebugMenuRootWidget* UGameDebugMenuFunctions::GetGameDebugMenuRootWidget(const UObject* WorldContextObject) { AGameDebugMenuManager* GDMManager = GetGameDebugMenuManager(WorldContextObject); if(!IsValid(GDMManager)) { return nullptr; } return GDMManager->GetDebugMenuRootWidget(); } bool UGameDebugMenuFunctions::RegisterGDMObjectProperty(UObject* TargetObject, const FGDMPropertyUIConfigInfo PropertyUIConfigInfo, const FName PropertyName, const FGDMGameplayCategoryKey CategoryKey, const FString PropertySaveKey, const FText DisplayPropertyName,const FText Description, const int32 DisplayPriority) { if(bDisableGameDebugMenu) { UE_LOG(LogGDM, Warning, TEXT("RegisterGDMObjectProperty: GameDebugMenu is disabled")); return false; } AGameDebugMenuManager* GDMManager = GetGameDebugMenuManager(TargetObject); if(!IsValid(GDMManager)) { /* まだ生成してないので一時キャッシュ */ FGDMPendingObjectData PendingData; PendingData.TargetObject = TargetObject; PendingData.TargetName = PropertyName; PendingData.CategoryKey = CategoryKey; PendingData.SaveKey = PropertySaveKey; PendingData.DisplayPropertyName = DisplayPropertyName; PendingData.Description = Description; PendingData.ConfigInfo = PropertyUIConfigInfo; PendingData.DisplayPriority = DisplayPriority; RegisterPendingProperties.Add(PendingData); return false; } return GDMManager->RegisterObjectProperty(TargetObject, PropertyName, CategoryKey, PropertySaveKey, DisplayPropertyName, Description, PropertyUIConfigInfo, DisplayPriority); } bool UGameDebugMenuFunctions::RegisterGDMObjectFunction(UObject* TargetObject, FName FunctionName, const FGDMGameplayCategoryKey CategoryKey, const FString FunctionSaveKey, const FText DisplayFunctionName,const FText Description, const int32 DisplayPriority) { if(bDisableGameDebugMenu) { UE_LOG(LogGDM, Warning, TEXT("RegisterGDMObjectFunction: GameDebugMenu is disabled")); return false; } AGameDebugMenuManager* GDMManager = GetGameDebugMenuManager(TargetObject); if(!IsValid(GDMManager)) { /* まだ生成してないので一時キャッシュ */ FGDMPendingObjectData PendingData; PendingData.TargetObject = TargetObject; PendingData.TargetName = FunctionName; PendingData.CategoryKey = CategoryKey; PendingData.SaveKey = FunctionSaveKey; PendingData.DisplayPropertyName = DisplayFunctionName; PendingData.Description = Description; PendingData.DisplayPriority = DisplayPriority; RegisterPendingFunctions.Add(PendingData); return false; } return GDMManager->RegisterObjectFunction(TargetObject, FunctionName, CategoryKey, FunctionSaveKey, DisplayFunctionName, Description, DisplayPriority); } void UGameDebugMenuFunctions::UnregisterGDMObject(UObject* TargetObject) { AGameDebugMenuManager* GDMManager = GetGameDebugMenuManager(TargetObject); if(!IsValid(GDMManager)) { return; } FGDMGameplayCategoryKey CategoryKey; FString PropertySaveKey; FText DisplayPropertyName; FText Description; FName PropertyName; EGDMPropertyType PropertyType; FString EnumPathName; FGDMPropertyUIConfigInfo PropertyUIConfigInfo; for(int32 Index = GDMManager->GetNumObjectProperties() - 1; Index >= 0; --Index) { UObject* RegisterObject = GDMManager->GetObjectProperty(Index, CategoryKey, PropertySaveKey, DisplayPropertyName, Description, PropertyName, PropertyType, EnumPathName, PropertyUIConfigInfo); if(!IsValid(RegisterObject) || (TargetObject == RegisterObject)) { GDMManager->RemoveObjectProperty(Index); } } FText DisplayFunctionName; FName FunctionName; for(int32 Index = GDMManager->GetNumObjectFunctions() - 1; Index >= 0; --Index) { UObject* RegisterObject = GDMManager->GetObjectFunction(Index, CategoryKey, PropertySaveKey, DisplayFunctionName, Description, FunctionName); if(!IsValid(RegisterObject) || (TargetObject == RegisterObject)) { GDMManager->RemoveObjectFunction(Index); } } } UObject* UGameDebugMenuFunctions::GetGDMObjectProperty(UObject* WorldContextObject,const int32 Index, FGDMGameplayCategoryKey& OutCategoryKey, FString& OutPropertySaveKey, FText& OutDisplayPropertyName, FText& OutDescription, FName& OutPropertyName, EGDMPropertyType& OutPropertyType, FString& OutEnumPathName, FGDMPropertyUIConfigInfo& PropertyUIConfigInfo) { AGameDebugMenuManager* GDMManager = GetGameDebugMenuManager(WorldContextObject); if(!IsValid(GDMManager)) { return nullptr; } return GDMManager->GetObjectProperty(Index, OutCategoryKey, OutPropertySaveKey, OutDisplayPropertyName, OutDescription, OutPropertyName, OutPropertyType, OutEnumPathName, PropertyUIConfigInfo); } UObject* UGameDebugMenuFunctions::GetGDMObjectFunction(UObject* WorldContextObject,const int32 Index, FGDMGameplayCategoryKey& OutCategoryKey, FString& OutFunctionSaveKey, FText& OutDisplayFunctionName, FText& OutDescription, FName& OutFunctionName) { AGameDebugMenuManager* GDMManager = GetGameDebugMenuManager(WorldContextObject); if(!IsValid(GDMManager)) { return nullptr; } return GDMManager->GetObjectFunction(Index, OutCategoryKey, OutFunctionSaveKey, OutDisplayFunctionName, OutDescription, OutFunctionName); } int32 UGameDebugMenuFunctions::GetGDMNumObjectProperties(UObject* WorldContextObject) { AGameDebugMenuManager* GDMManager = GetGameDebugMenuManager(WorldContextObject); if(!IsValid(GDMManager)) { return 0; } return GDMManager->GetNumObjectProperties(); } int32 UGameDebugMenuFunctions::GetGDMNumObjectFunctions(UObject* WorldContextObject) { AGameDebugMenuManager* GDMManager = GetGameDebugMenuManager(WorldContextObject); if(!IsValid(GDMManager)) { return 0; } return GDMManager->GetNumObjectFunctions(); } bool UGameDebugMenuFunctions::VerifyGDMNumObjectProperties(UObject* WorldContextObject) { UE_LOG(LogGDM,Verbose, TEXT("VerifyGDMNumObjectProperties")); AGameDebugMenuManager* GDMManager = GetGameDebugMenuManager(WorldContextObject); if(!IsValid(GDMManager)) { return false; } FGDMGameplayCategoryKey CategoryKey; FString PropertySaveKey; FText DisplayPropertyName; FText Description; FName PropertyName; EGDMPropertyType PropertyType; FString EnumPathName; FGDMPropertyUIConfigInfo PropertyUIConfigInfo; int32 RemoveCount = 0; for(int32 Index = GetGDMNumObjectProperties(WorldContextObject) - 1; Index >= 0; --Index) { const UObject* PropertyOwnerObj = GetGDMObjectProperty(WorldContextObject, Index, CategoryKey, PropertySaveKey, DisplayPropertyName, Description, PropertyName, PropertyType, EnumPathName, PropertyUIConfigInfo); if(!IsValid(PropertyOwnerObj)) { GDMManager->RemoveObjectProperty(Index); RemoveCount++; } } UE_LOG(LogGDM,Verbose, TEXT("VerifyGDMNumObjectProperties: Remove %d"), RemoveCount); return (RemoveCount != 0); } bool UGameDebugMenuFunctions::VerifyGDMNumObjectFunctions(UObject* WorldContextObject) { UE_LOG(LogGDM,Verbose, TEXT("VerifyGDMNumObjectFunctions")); AGameDebugMenuManager* GDMManager = GetGameDebugMenuManager(WorldContextObject); if(!IsValid(GDMManager)) { return false; } const int32 Num = GetGDMNumObjectFunctions(WorldContextObject); FGDMGameplayCategoryKey CategoryKey; FString FunctionSaveKey; FText DisplayFunctionName; FText Description; FName FunctionName; int32 RemoveCount = 0; for(int32 Index = Num - 1; Index >= 0; --Index) { const UObject* FunctionOwnerObj = GetGDMObjectFunction(WorldContextObject,Index, CategoryKey,FunctionSaveKey, DisplayFunctionName,Description,FunctionName); if(!IsValid(FunctionOwnerObj)) { GDMManager->RemoveObjectFunction(Index); RemoveCount++; } } UE_LOG(LogGDM,Verbose, TEXT("VerifyGDMNumObjectFunctions: Remove %d"), RemoveCount); return (RemoveCount == 0); } TArray UGameDebugMenuFunctions::GetGDMOrderGameplayCategoryTitle(UObject* WorldContextObject) { TArray ReturnValues; const auto& TitleArray = GetDefault()->OrderGameplayCategoryTitles; for( int32 Index = 0; Index < TitleArray.Num(); ++Index ) { ReturnValues.Add(FGDMMenuCategoryKey(TitleArray[Index].Index, TitleArray[Index].Title)); } return ReturnValues; } int32 UGameDebugMenuFunctions::GetGDMInvalidIndex() { return INDEX_NONE; } FString UGameDebugMenuFunctions::GetGDMObjectName(UObject* TargetObject) { if(!IsValid(TargetObject)) { return FString(); } #if WITH_EDITOR return UKismetSystemLibrary::GetDisplayName(TargetObject); #else return UKismetSystemLibrary::GetObjectName(TargetObject); #endif } void UGameDebugMenuFunctions::RegisterInputComponentForGameDebugMenu(UObject* WorldContextObject, UInputComponent* InputComponent) { if (AGameDebugMenuManager* GDMManager = GetGameDebugMenuManager(WorldContextObject)) { GDMManager->GetDebugMenuInputSystemComponent()->RegisterInputComponent(InputComponent); } } void UGameDebugMenuFunctions::UnregisterInputComponentForGameDebugMenu(UObject* WorldContextObject, UInputComponent* InputComponent) { if (AGameDebugMenuManager* GDMManager = GetGameDebugMenuManager(WorldContextObject)) { GDMManager->GetDebugMenuInputSystemComponent()->UnregisterInputComponent(InputComponent); } } bool UGameDebugMenuFunctions::ShowDebugReport(UObject* WorldContextObject) { AGameDebugMenuManager* GDMManager = GetGameDebugMenuManager(WorldContextObject); if(!IsValid(GDMManager)) { UE_LOG(LogGDM, Log, TEXT("RequestDebugReport: Instance not found")); return false; } if(GDMManager->ShowDebugMenu(true)) { return true; } return false; } EGDMProjectManagementTool UGameDebugMenuFunctions::GetGDMSelectedProjectManagementTool() { return GetDefault()->ProjectManagementToolType; } FString UGameDebugMenuFunctions::GetGDMBuildConfigurationString() { #if UE_EDITOR return TEXT("Editor"); #else return LexToString(FApp::GetBuildConfiguration()); #endif } FString UGameDebugMenuFunctions::GetGDMBuildVersionString() { return FApp::GetBuildVersion(); } FString UGameDebugMenuFunctions::GetGDMProjectVersionString() { const UGeneralProjectSettings& ProjectSettings = *GetDefault(); return *ProjectSettings.ProjectVersion; } TArray UGameDebugMenuFunctions::GetGDMCultureList() { return GetDefault()->CultureList; } void UGameDebugMenuFunctions::PrintLogScreen(UObject* WorldContextObject, const FString& InString, float Duration, bool bPrintToLog) { #if !(UE_BUILD_SHIPPING) static FLinearColor LogColor(0.15f, 0.2f, 0.2f); const UWorld* World = GEngine->GetWorldFromContextObject(WorldContextObject, EGetWorldErrorMode::ReturnNull); FString Prefix; if (World) { if (World->WorldType == EWorldType::PIE) { switch (World->GetNetMode()) { case NM_Client: { /* UE::GetPlayInEditorID() はマルチPIEで "ambiguous PIE world" 警告を出すことがあるので Worldに紐づくPIE Instance Idを使用する*/ const UPackage* Pkg = World->GetPackage(); const int32 PieInstanceId = Pkg ? Pkg->GetPIEInstanceID() : INDEX_NONE; Prefix = FString::Printf(TEXT("Client PIE_%d: "), PieInstanceId); } break; case NM_DedicatedServer: case NM_ListenServer: Prefix = FString::Printf(TEXT("Server: ")); break; case NM_Standalone: break; default: ; } } } const FString FinalDisplayString = Prefix + InString; FString FinalLogString = FinalDisplayString; static const FBoolConfigValueHelper DisplayPrintStringSource(TEXT("Kismet"), TEXT("bLogPrintStringSource"), GEngineIni); if (DisplayPrintStringSource) { const FString SourceObjectPrefix = FString::Printf(TEXT("[%s] "), *GetNameSafe(WorldContextObject)); FinalLogString = SourceObjectPrefix + FinalLogString; } if (GAreScreenMessagesEnabled) { if (GConfig && Duration < 0) { GConfig->GetFloat(TEXT("Kismet"), TEXT("PrintStringDuration"), Duration, GEngineIni); } GEngine->AddOnScreenDebugMessage((uint64)-1, Duration, LogColor.ToFColor(true), TEXT("GDM: ") + FinalDisplayString); } else { UE_LOG(LogGDM, Log, TEXT("Screen messages disabled (!GAreScreenMessagesEnabled). Cannot print to screen.")); } if( bPrintToLog ) { UE_LOG(LogGDM, Log, TEXT("%s"), *FinalDisplayString); } else { UE_LOG(LogGDM, Verbose, TEXT("%s"), *FinalLogString); } #endif } bool UGameDebugMenuFunctions::GetDebugMenuString(UObject* WorldContextObject, const FString StringKey, FString& OutString) { if(const AGameDebugMenuManager* Manager = GetGameDebugMenuManager(WorldContextObject) ) { return Manager->GetLocalizeStringComponent()->GetString(StringKey, OutString); } OutString = StringKey; return false; } FName UGameDebugMenuFunctions::GetCurrentDebugMenuLanguage(UObject* WorldContextObject) { if(const AGameDebugMenuManager* Manager = GetGameDebugMenuManager(WorldContextObject) ) { return Manager->GetLocalizeStringComponent()->GetCurrentDebugMenuLanguage(); } return NAME_None; } TArray UGameDebugMenuFunctions::GetDebugMenuLanguageKeys() { return GetDefault()->GetDebugMenuLanguageKeys(); } FString UGameDebugMenuFunctions::GetDebugMenuLineBreakString() { return GetDefault()->LineBreakString; } FString UGameDebugMenuFunctions::BuildConsoleCommandId_FromSingle(const FGDMConsoleCommandSingle& Command) { return Command.BuildCommandIdentifier(); } FString UGameDebugMenuFunctions::BuildConsoleCommandId_FromGroup(const FGDMConsoleCommandGroup& Command) { return Command.BuildCommandIdentifier(); } FString UGameDebugMenuFunctions::BuildConsoleCommandId_FromPair(const FGDMConsoleCommandPair& Command) { return Command.BuildCommandIdentifier(); } FString UGameDebugMenuFunctions::BuildConsoleCommandId_FromNumber(const FGDMConsoleCommandNumber& Command) { return Command.BuildCommandIdentifier(); } bool UGameDebugMenuFunctions::EqualEqual_GDMMenuCategoryKey(const FGDMMenuCategoryKey& A, const FGDMMenuCategoryKey& B) { return A.Index == B.Index; } bool UGameDebugMenuFunctions::NotEqual_GDMMenuCategoryKey(const FGDMMenuCategoryKey& A, const FGDMMenuCategoryKey& B) { return A.Index != B.Index; } uint8 UGameDebugMenuFunctions::Conv_GDMMenuCategoryKeyToByte(const FGDMMenuCategoryKey& Key) { return Key.Index; } FGDMMenuCategoryKey UGameDebugMenuFunctions::Conv_ByteToGDMMenuCategoryKey(const uint8& Index) { return FGDMMenuCategoryKey(Index); } void UGameDebugMenuFunctions::OnActorSpawnedClientWaitManager(AGameDebugMenuManager* SpawnDebugMenuManager) { /* マネージャー生成前に登録処理したプロパティ群を追加する */ for(const auto& PendingData : RegisterPendingProperties) { if(PendingData.TargetObject.IsValid()) { if( PendingData.TargetObject->GetWorld() == SpawnDebugMenuManager->GetWorld() ) { RegisterGDMObjectProperty(PendingData.TargetObject.Get() ,PendingData.ConfigInfo ,PendingData.TargetName ,PendingData.CategoryKey ,PendingData.SaveKey ,PendingData.DisplayPropertyName ,PendingData.Description ,PendingData.DisplayPriority ); } } } /* マネージャー生成前に登録処理した関数群を追加する */ for(const auto& PendingData : RegisterPendingFunctions) { if(PendingData.TargetObject.IsValid()) { if( PendingData.TargetObject->GetWorld() == SpawnDebugMenuManager->GetWorld() ) { RegisterGDMObjectFunction(PendingData.TargetObject.Get() ,PendingData.TargetName ,PendingData.CategoryKey ,PendingData.SaveKey ,PendingData.DisplayPropertyName ,PendingData.Description ,PendingData.DisplayPriority ); } } } RegisterPendingProperties.RemoveAll([&](const FGDMPendingObjectData& PropertyData) { if (!PropertyData.TargetObject.IsValid()) { return true; } return (PropertyData.TargetObject->GetWorld() == SpawnDebugMenuManager->GetWorld()); }); RegisterPendingFunctions.RemoveAll([&](const FGDMPendingObjectData& FunctionData) { if (!FunctionData.TargetObject.IsValid()) { return true; } return (FunctionData.TargetObject->GetWorld() == SpawnDebugMenuManager->GetWorld()); }); UE_LOG(LogGDM, Log, TEXT("OnActorSpawnedClientWaitManager: Spawn GameDebugMenuManager")); } void UGameDebugMenuFunctions::OnActorSpawnedServer(AActor* SpawnActor) { if (!IsValid(SpawnActor)) { return; } if (!SpawnActor->IsA(APlayerController::StaticClass())) { return; } AGameDebugMenuManager* DebugMenuManager = GetGameDebugMenuManager(SpawnActor); if (!IsValid(DebugMenuManager)) { UE_LOG(LogGDM, Warning, TEXT("OnActorSpawnedServer: Not found DebugMenuManager")); return; } DebugMenuManager->AddDebugMenuPCProxyComponent(Cast(SpawnActor)); } void UGameDebugMenuFunctions::ShowDebugConsoleCommand() { for (const auto Manager : GGameDebugMenuManagers ) { if (IsValid(Manager->GetWorld()) && IsValid(Manager->GetOwner())) { Manager->ShowDebugMenu(); break; } } } void UGameDebugMenuFunctions::HideDebugConsoleCommand() { for (const auto Manager : GGameDebugMenuManagers ) { if (IsValid(Manager->GetWorld()) && IsValid(Manager->GetOwner())) { Manager->HideDebugMenu(); break; } } } void UGameDebugMenuFunctions::ToggleDebugConsoleCommand() { for (const auto Manager : GGameDebugMenuManagers ) { if (IsValid(Manager->GetWorld()) && IsValid(Manager->GetOwner())) { if( Manager->IsShowingDebugMenu()) { Manager->HideDebugMenu(); } else { Manager->ShowDebugMenu(); } break; } } } void UGameDebugMenuFunctions::ToggleInputSystemLog() { for (const auto Manager : GGameDebugMenuManagers ) { if (IsValid(Manager->GetWorld()) && IsValid(Manager->GetOwner())) { UGDMInputSystemComponent* InputSystemComponent = Manager->GetDebugMenuInputSystemComponent(); InputSystemComponent->bOutputDebugLog = !InputSystemComponent->bOutputDebugLog; break; } } } ``` `Plugin/GameDebugMenu/Source/GameDebugMenu/Private/GameDebugMenuManager.cpp`: ```cpp /** * Copyright (c) 2020 akihiko moroi * * This software is released under the MIT License. * (See accompanying file LICENSE.txt or copy at http://opensource.org/licenses/MIT) */ #include "GameDebugMenuManager.h" #include "Engine/DebugCameraController.h" #include "GameFramework/CheatManager.h" #include "GameFramework/WorldSettings.h" #include "Kismet/KismetStringLibrary.h" #include "Kismet/KismetSystemLibrary.h" #include "Kismet/GameplayStatics.h" #include "TimerManager.h" #include "Blueprint/GameViewportSubsystem.h" #include "Blueprint/WidgetBlueprintLibrary.h" #include "Framework/Application/SlateApplication.h" #include "GameDebugMenuSettings.h" #include "GameDebugMenuFunctions.h" #include "Log/GDMOutputDevice.h" #include "Component/GDMListenerComponent.h" #include "Component/GDMPlayerControllerProxyComponent.h" #include "Component/GDMScreenshotRequesterComponent.h" #include "Component/GDMLocalizeStringComponent.h" #include "Component/GDMPropertyJsonSystemComponent.h" #include "Component/GDMSaveSystemComponent.h" #include "ConsoleCommand/GDMConsoleCommandValueProviderComponent.h" #include "Input/GDMInputSystemComponent.h" #include "Widgets/GameDebugMenuRootWidget.h" #include "Widgets/GDMTextBlock.h" #include "Data/GameDebugMenuManagerAsset.h" #include "Favorite/GDMFavoriteSystemComponent.h" /********************************************************************/ /* AGameDebugMenuManager */ /********************************************************************/ AGameDebugMenuManager::AGameDebugMenuManager(const FObjectInitializer& ObjectInitializer) : Super(ObjectInitializer) , DebugMenuInputSystemComponent(nullptr) , ScreenshotRequesterComponent(nullptr) , PropertyJsonSystemComponent(nullptr) , SaveSystemComponent(nullptr) , LocalizeStringComponent(nullptr) , ListenerComponent(nullptr) , InitializeManagerHandle() , bInitializedManager(false) , MenuAsset(nullptr) , ConsoleCommandSetAsset(nullptr) , bShowDebugMenu(false) , bCachedGamePaused(false) , bCachedShowMouseCursor(false) , bWaitToCaptureBeforeOpeningDebugReportMenu(false) , CachedNavigationConfigs() , ObjectProperties() , ObjectFunctions() , DebugMenuRootWidget(nullptr) , DebugMenuInstances() , OutputLog(nullptr) { DebugMenuInputSystemComponent = CreateDefaultSubobject(TEXT("DebugMenuInputSystemComponent")); ScreenshotRequesterComponent = CreateDefaultSubobject(TEXT("ScreenshotRequesterComponent")); PropertyJsonSystemComponent = CreateDefaultSubobject(TEXT("PropertyJsonSystemComponent")); SaveSystemComponent = CreateDefaultSubobject(TEXT("SaveSystemComponent")); FavoriteSystemComponent = CreateDefaultSubobject(TEXT("FavoriteSystemComponent")); ConsoleCommandValueProviderComponent = CreateDefaultSubobject(TEXT("ConsoleCommandValueProviderComponent"));; LocalizeStringComponent = CreateDefaultSubobject(TEXT("LocalizeStringComponent")); ListenerComponent = CreateDefaultSubobject(TEXT("ListenerComponent")); PrimaryActorTick.bCanEverTick = true; PrimaryActorTick.bStartWithTickEnabled = true; PrimaryActorTick.bTickEvenWhenPaused = true; SetCanBeDamaged(false); SetHidden(true); InputPriority = TNumericLimits::Max(); bBlockInput = false; SetRemoteRoleForBackwardsCompat(ROLE_SimulatedProxy); bReplicates = true; bAlwaysRelevant = false; SetReplicatingMovement(false); SetNetUpdateFrequency(1.0f);/* デフォルトのPlayerStateと同じかんじにしとく */ bOnlyRelevantToOwner = true; } void AGameDebugMenuManager::BeginPlay() { Super::BeginPlay(); /* DedicatedServer ではローカルUIが無いので何もしない */ if (UKismetSystemLibrary::IsDedicatedServer(this)) { return; } /* BeginPlay時点でOwnerが未確定なケースがある(レプリケーション生成など) * Ownerが確定して「ローカルPCのManager」であることが分かったときだけ初期化する。*/ auto TryLocalInit = [this]() { if (bLocalBeginPlayInitialized) { return; } const APlayerController* PC = Cast(GetOwner()); if (!IsValid(PC)) { return; /* まだOwner未確定、後で再試行 */ } if (!PC->IsLocalController()) { /* Ownerが確定していてローカルでないなら、このManagerはこのプロセスでは初期化しない */ bLocalBeginPlayInitialized = true; return; } bLocalBeginPlayInitialized = true; OutputLog = MakeShared(); /* 他で参照される前にロードは処理しとく */ GetSaveSystemComponent()->LoadDebugMenuFile(); UGameDebugMenuFunctions::RegisterGameDebugMenuManagerInstance(this); UGameDebugMenuFunctions::PrintLogScreen(this, TEXT("AGameDebugMenuManager: Call BeginPlay"), 4.0f); /* managerのBeginplayがちゃんと完了後に処理↓ */ GetWorld()->GetTimerManager().SetTimer(InitializeManagerHandle, FTimerDelegate::CreateLambda([this]() { OnInitializeManager(); }), 0.01f, true); }; TryLocalInit(); /* Owner未確定なら短時間だけリトライ(Ownerが確定したら止まる) */ if (!bLocalBeginPlayInitialized) { GetWorld()->GetTimerManager().SetTimer(LocalBeginPlayRetryHandle, FTimerDelegate::CreateLambda([this, TryLocalInit]() { TryLocalInit(); if (bLocalBeginPlayInitialized) { GetWorld()->GetTimerManager().ClearTimer(LocalBeginPlayRetryHandle); } }), 0.1f, true); } } void AGameDebugMenuManager::EndPlay(const EEndPlayReason::Type EndPlayReason) { UGameDebugMenuFunctions::PrintLogScreen(this, TEXT("AGameDebugMenuManager: Call EndPlay"), 4.0f); OutputLog.Reset(); if(EndPlayReason != EEndPlayReason::EndPlayInEditor && EndPlayReason != EEndPlayReason::Quit) { if (const UWorld* World = GetWorld()) { World->GetTimerManager().ClearTimer(InitializeManagerHandle); } EnabledNavigationConfigs(); if(IsValid(DebugMenuRootWidget)) { DebugMenuRootWidget->RemoveFromParent(); DebugMenuRootWidget = nullptr; } } Super::EndPlay(EndPlayReason); UGameDebugMenuFunctions::UnregisterGameDebugMenuManagerInstance(this); } void AGameDebugMenuManager::Tick(float DeltaTime) { /* Slomoなど時間操作に影響受けないようにする */ DeltaTime /= GetWorldSettings()->GetEffectiveTimeDilation(); Super::Tick(DeltaTime); } UGDMInputSystemComponent* AGameDebugMenuManager::GetDebugMenuInputSystemComponent() const { return DebugMenuInputSystemComponent; } UGDMScreenshotRequesterComponent* AGameDebugMenuManager::GetScreenshotRequesterComponent() const { return ScreenshotRequesterComponent; } UGDMPropertyJsonSystemComponent* AGameDebugMenuManager::GetPropertyJsonSystemComponent() const { return PropertyJsonSystemComponent; } UGDMSaveSystemComponent* AGameDebugMenuManager::GetSaveSystemComponent() const { return SaveSystemComponent; } UGDMFavoriteSystemComponent* AGameDebugMenuManager::GetFavoriteSystemComponent() const { return FavoriteSystemComponent; } UGDMConsoleCommandValueProviderComponent* AGameDebugMenuManager::GetConsoleCommandValueProviderComponent() const { return ConsoleCommandValueProviderComponent; } UGDMLocalizeStringComponent* AGameDebugMenuManager::GetLocalizeStringComponent() const { return LocalizeStringComponent; } UGDMListenerComponent* AGameDebugMenuManager::GetListenerComponent() const { return ListenerComponent; } bool AGameDebugMenuManager::IsInitializedManager() const { return bInitializedManager; } void AGameDebugMenuManager::OnInitializeManager() { if (bInitializedManager) { UGameDebugMenuFunctions::PrintLogScreen(this, TEXT("AGameDebugMenuManager: The manager has already been initialized"), 0.6f); return; } if (GetOwner() == nullptr) { UGameDebugMenuFunctions::PrintLogScreen(this, TEXT("AGameDebugMenuManager: Not found Owner"), 0.6f); return; } APlayerController* PC = Cast(GetOwner()); if (!IsValid(PC)) { UGameDebugMenuFunctions::PrintLogScreen(this, TEXT("AGameDebugMenuManager: The owner is not a PlayerController"), 0.6f); return; } if (!IsValid(PC->GetLocalPlayer())) { if (PC->IsLocalController()) { UGameDebugMenuFunctions::PrintLogScreen(this, TEXT("AGameDebugMenuManager: No LocalPlayer"), 0.6f); return; } } /* BP側の準備完了を待つ(例:必要なアセットロード待ちなど) */ if (!IsReadyToInitializeManager()) { return; } bInitializedManager = true; GetLocalizeStringComponent()->SetJsonSystemComponentValue(GetPropertyJsonSystemComponent()); GetLocalizeStringComponent()->SyncLoadDebugMenuStringTables(); if( !UKismetSystemLibrary::IsDedicatedServer(this) && PC->IsLocalController() ) { GetFavoriteSystemComponent()->Initialize(MenuAsset); CreateDebugMenuRootWidget(); GetDebugMenuInputSystemComponent()->Initialize(MenuAsset); TArray FoundWidgets; UWidgetBlueprintLibrary::GetAllWidgetsOfClass(this, FoundWidgets, UGameDebugMenuWidget::StaticClass(), true); UGameViewportSubsystem* GameViewportSubsystem = UGameViewportSubsystem::Get(); GameViewportSubsystem->OnWidgetAdded.AddUObject(this, &AGameDebugMenuManager::OnWidgetAdded); GameViewportSubsystem->OnWidgetRemoved.AddUObject(this, &AGameDebugMenuManager::OnWidgetRemoved); const ULocalPlayer* OwnerLocalPlayer = PC->GetLocalPlayer(); for(const auto W : FoundWidgets) { if(UGameDebugMenuWidget* DebugMenuWidget = Cast(W)) { /* マルチPIE(複数ウィンドウ/複数World)では、全Widget列挙が他World/他LocalPlayerを混ぜることがある。 このManagerのOwner LocalPlayerに紐づくWidgetだけを対象にする*/ if (!IsValid(OwnerLocalPlayer) || DebugMenuWidget->GetOwningLocalPlayer() == OwnerLocalPlayer) { ViewportDebugMenuWidgets.AddUnique(DebugMenuWidget); } } } } if (!IsValid(PC->CheatManager)) { if (GetNetMode() == NM_Client) { /* クライアントでも利用できるように常に有効化 */ PC->EnableCheats(); } } TArray CommandHistory = GetPropertyJsonSystemComponent()->GetCustomStringArray(TEXT("CommandHistory")); for(const auto& Command : CommandHistory ) { PC->ConsoleCommand(Command); } GetWorld()->GetTimerManager().ClearTimer(InitializeManagerHandle); EnableInput(PC); OnInitializeManagerBP(); } void AGameDebugMenuManager::CreateDebugMenuRootWidget() { if( !IsValid(MenuAsset) ) { UE_LOG(LogGDM, Warning, TEXT("CreateDebugMenuRootWidget: Not found MenuAsset")); return; } if( IsValid(DebugMenuRootWidget) ) { DebugMenuRootWidget->RemoveFromParent(); } DebugMenuRootWidget = Cast(UWidgetBlueprintLibrary::Create(this, MenuAsset->DebugMenuRootWidgetClass, GetOwnerPlayerController())); DebugMenuRootWidget->SetDebugMenuManager(this); DebugMenuRootWidget->AddToViewport(MenuAsset->RootWidgetZOrder); DebugMenuRootWidget->SetVisibility(ESlateVisibility::Collapsed); DebugMenuRootWidget->EnsureDebugMenuInputComponent(); DebugMenuRootWidget->InitializeRootWidget(); } void AGameDebugMenuManager::EnabledNavigationConfigs() { if( CachedNavigationConfigs.Num() > 0 ) { /* Navigationを戻す */ FSlateApplication::Get().SetNavigationConfig(CachedNavigationConfigs[0]); CachedNavigationConfigs.Empty(); } } void AGameDebugMenuManager::DisabledNavigationConfigs() { /* Navigationを切る */ CachedNavigationConfigs.Empty(); CachedNavigationConfigs.Add(FSlateApplication::Get().GetNavigationConfig()); FSlateApplication::Get().SetNavigationConfig(MakeShared()); } void AGameDebugMenuManager::EnableShowMouseCursorFlag(APlayerController* PlayerController) { bCachedShowMouseCursor = PlayerController->bShowMouseCursor; PlayerController->bShowMouseCursor = true; if(!IsValid(PlayerController->CheatManager)) { return; } if(!IsValid(PlayerController->CheatManager->DebugCameraControllerRef)) { return; } /* DebugCamera操作中Menuを出すとマウスが表示されないのでこっちも合わせて更新 */ PlayerController->CheatManager->DebugCameraControllerRef->bShowMouseCursor = true; } void AGameDebugMenuManager::RestoreShowMouseCursorFlag(APlayerController* PlayerController) const { PlayerController->bShowMouseCursor = bCachedShowMouseCursor; if(!IsValid(PlayerController->CheatManager)) { return; } if(!IsValid(PlayerController->CheatManager->DebugCameraControllerRef)) { return; } /* DebugCameraも合わせて更新 */ PlayerController->CheatManager->DebugCameraControllerRef->bShowMouseCursor = bCachedShowMouseCursor; } void AGameDebugMenuManager::TryEnableGamePause() { check(IsValid(MenuAsset)); if(MenuAsset->bGamePause) { bCachedGamePaused = UGameplayStatics::IsGamePaused(this); UGameplayStatics::SetGamePaused(this, true); } } void AGameDebugMenuManager::RestoreGamePause() const { check(IsValid(MenuAsset)); if(MenuAsset->bGamePause) { UGameplayStatics::SetGamePaused(this, bCachedGamePaused); } } void AGameDebugMenuManager::OnScreenshotRequestProcessed() { bWaitToCaptureBeforeOpeningDebugReportMenu = false; GetListenerComponent()->OnScreenshotRequestProcessedDispatcher.RemoveDynamic(this, &AGameDebugMenuManager::OnScreenshotRequestProcessed); SetIgnoreInput(false); GetDebugMenuInputSystemComponent()->OnOpenMenu(); TArray DebugMenuWidgets = GetViewportDebugMenuWidgets(); for(const auto ViewportWidget : DebugMenuWidgets ) { if( ViewportWidget->IsActivateDebugMenu() ) { ViewportWidget->OnShowingMenu(/* bRequestDebugMenuManager */true); } } GetDebugMenuRootWidget()->ShowDebugReport(); CallShowDispatcher(); } void AGameDebugMenuManager::ExecuteConsoleCommand(const FString& Command, APlayerController* PC) { if(Command.IsEmpty()) { return; } const UWorld* World = GetWorld(); if(!IsValid(World)) { return; } if(!IsValid(PC)) { return; } FString LogCommand; if(IsValid(PC->CheatManager)) { if(ADebugCameraController* DCC = PC->CheatManager->DebugCameraControllerRef) { if(!DCC->IsInState(NAME_Inactive)) { /* DebugCameraControllerが動作してた場合はそっちで実行 */ LogCommand = DCC->ConsoleCommand(Command); UGameDebugMenuFunctions::PrintLogScreen(this, FString::Printf(TEXT("ExecuteConsoleCommand DCC: %s: Log %s"), *Command, *LogCommand), 4.0f); CallExecuteConsoleCommandDispatcher(Command); return; } } } LogCommand = PC->ConsoleCommand(Command); UGameDebugMenuFunctions::PrintLogScreen(this, FString::Printf(TEXT("ExecuteConsoleCommand PC: %s: Log %s"), *Command, *LogCommand), 4.0f); CallExecuteConsoleCommandDispatcher(Command); } bool AGameDebugMenuManager::ShowDebugMenu(bool bWaitToCaptureBeforeOpeningMenuFlag) { if(bShowDebugMenu) { UGameDebugMenuFunctions::PrintLogScreen(this, TEXT("ShowDebugMenu: bShowDebugMenu TRUE"), 4.0f); return false; } const UWorld* World = GetWorld(); if(!IsValid(World)) { UGameDebugMenuFunctions::PrintLogScreen(this, TEXT("ShowDebugMenu: Not found World"), 4.0f); return false; } if(!IsValid(DebugMenuRootWidget)) { UGameDebugMenuFunctions::PrintLogScreen(this, TEXT("ShowDebugMenu: Not found DebugMenuRootWidget"), 4.0f); return false; } bShowDebugMenu = true; APlayerController* PC = GetOwnerPlayerController(); DisabledNavigationConfigs(); EnableShowMouseCursorFlag(PC); TryEnableGamePause(); bool bShow = true; if( !GetDefault()->bDisableScreenCaptureProcessingWhenOpeningDebugMenu ) { GetScreenshotRequesterComponent()->RequestScreenshot(); if( bWaitToCaptureBeforeOpeningMenuFlag ) { /* 画面キャプチャ後UIを出す必要がある */ /* このフレームではメニューを開かない */ bShow = false; /* キャプチャ後メニューを開けるようにするための識別フラグをたてる */ bWaitToCaptureBeforeOpeningDebugReportMenu = true; /* 開くまで入力を無視するようにする */ SetIgnoreInput(true); GetListenerComponent()->OnScreenshotRequestProcessedDispatcher.AddDynamic(this, &AGameDebugMenuManager::OnScreenshotRequestProcessed); } } if( bShow ) { GetDebugMenuInputSystemComponent()->OnOpenMenu(); TArray DebugMenuWidgets = GetViewportDebugMenuWidgets(); for(const auto ViewportWidget : DebugMenuWidgets ) { if( ViewportWidget->IsActivateDebugMenu() ) { ViewportWidget->OnShowingMenu(/* bRequestDebugMenuManager */true); } } CallShowDispatcher(); UE_LOG(LogGDM, Log, TEXT("ShowDebugMenu")); } return true; } void AGameDebugMenuManager::HideDebugMenu() { if(!bShowDebugMenu) { UGameDebugMenuFunctions::PrintLogScreen(this, TEXT("HideDebugMenu: bShowDebugMenu FALSE"), 4.0f); return; } if(bWaitToCaptureBeforeOpeningDebugReportMenu) { /* キャプチャ後1度UIを開く予定なので閉じないようにする */ UGameDebugMenuFunctions::PrintLogScreen(this, TEXT("HideDebugMenu: bWaitToCaptureBeforeOpeningMenu TRUE"), 4.0f); return; } const UWorld* World = GetWorld(); if(!IsValid(World)) { UGameDebugMenuFunctions::PrintLogScreen(this, TEXT("HideDebugMenu: Not found World"), 4.0f); return; } if(!IsValid(DebugMenuRootWidget)) { UGameDebugMenuFunctions::PrintLogScreen(this, TEXT("HideDebugMenu: Not found DebugMenuWidget"), 4.0f); return; } GetDebugMenuInputSystemComponent()->OnCloseMenu(); bShowDebugMenu = false; APlayerController* PC = GetOwnerPlayerController(); EnabledNavigationConfigs(); RestoreShowMouseCursorFlag(PC); RestoreGamePause(); GetSaveSystemComponent()->SaveDebugMenuFile(); TArray DebugMenuWidgets = GetViewportDebugMenuWidgets(); for(const auto ViewportWidget : DebugMenuWidgets ) { if( ViewportWidget->IsActivateDebugMenu() ) { ViewportWidget->OnHidingMenu(/* bRequestDebugMenuManager */true); } } CallHideDispatcher(); UE_LOG(LogGDM, Log, TEXT("HideDebugMenu")); } bool AGameDebugMenuManager::IsShowingDebugMenu() { return bShowDebugMenu; } APlayerController* AGameDebugMenuManager::GetOwnerPlayerController() const { checkf(IsValid(GetOwner()), TEXT("No owner")); checkf(IsValid(Cast(GetOwner())), TEXT("Owner needs to be PlayerController")); return Cast(GetOwner()); } UGameDebugMenuRootWidget* AGameDebugMenuManager::GetDebugMenuRootWidget() { return DebugMenuRootWidget; } int32 AGameDebugMenuManager::GetAllDebugMenuKeys(TArray& OutKeys) { if( IsValid(MenuAsset) ) { OutKeys = MenuAsset->DebugMenuRegistrationOrder; } else { UE_LOG(LogGDM, Warning, TEXT("GetAllDebugMenuKeys: Not found MenuAsset")); } return OutKeys.Num(); } TSubclassOf AGameDebugMenuManager::GetDebugMenuWidgetClass(const FString& Key) { if( IsValid(MenuAsset) ) { if(const TSubclassOf* WidgetClass = MenuAsset->DebugMenuClasses.Find(Key) ) { return (*WidgetClass); } } else { UE_LOG(LogGDM, Warning, TEXT("GetAllDebugMenuKeys: Not found MenuAsset")); } return nullptr; } FString AGameDebugMenuManager::GetDebugMenuWidgetKey(const UGameDebugMenuWidget* Widget) { for (const auto& Pair : DebugMenuInstances) { if( Pair.Value == Widget ) { return Pair.Key; } } return TEXT(""); } bool AGameDebugMenuManager::GetDebugMenuWidgetInstances(TArray& OutInstances) { TArray> Array; DebugMenuInstances.GenerateValueArray(Array); OutInstances.Reset(); OutInstances.Reserve(Array.Num()); for (auto& A : Array) { OutInstances.Add(A); } return (OutInstances.Num() > 0); } void AGameDebugMenuManager::GetOutputLogString(FString& OutLog, const FString& Separator) { OutLog = UKismetStringLibrary::JoinStringArray(OutputLog->GetLogs(), Separator); } void AGameDebugMenuManager::GetOutputCommandHistoryString(TArray& OutCommandHistory) { OutCommandHistory = OutputLog->GetCommandHistory(); } void AGameDebugMenuManager::ClearCommandHistory() { OutputLog->ClearCommandHistory(); } EGDMPropertyType AGameDebugMenuManager::GetPropertyType(const FProperty* TargetProperty) const { if(TargetProperty != nullptr) { /* メニューで対応できるプロパティかチェック */ if(TargetProperty->IsA(FBoolProperty::StaticClass())) { return EGDMPropertyType::GDM_Bool; } else if(TargetProperty->IsA(FIntProperty::StaticClass())) { return EGDMPropertyType::GDM_Int; } else if(TargetProperty->IsA(FFloatProperty::StaticClass())) { return EGDMPropertyType::GDM_Float; } else if(TargetProperty->IsA(FDoubleProperty::StaticClass())) { return EGDMPropertyType::GDM_Float; } else if(TargetProperty->IsA(FEnumProperty::StaticClass())) { return EGDMPropertyType::GDM_Enum; } else if(TargetProperty->IsA(FByteProperty::StaticClass())) { return EGDMPropertyType::GDM_Byte; } else if(TargetProperty->IsA(FStrProperty::StaticClass())) { return EGDMPropertyType::GDM_String; } else if( TargetProperty->IsA(FStructProperty::StaticClass()) ) { const FStructProperty* StructProp = CastFieldChecked(TargetProperty); if( StructProp->Struct->GetFName() == NAME_Vector2D ) { return EGDMPropertyType::GDM_Vector2D; } else if( StructProp->Struct->GetFName() == NAME_Vector ) { return EGDMPropertyType::GDM_Vector; } else if( StructProp->Struct->GetFName() == NAME_Rotator ) { return EGDMPropertyType::GDM_Rotator; } } } return EGDMPropertyType::GDM_Null; } #define SET_VARIANT_PROPERTY(PropertyClass) \ { \ OldValueVariant = CastField(Property)->GetPropertyValue(ValuePtr); \ } #define SET_VARIANT_STRUCT_PROPERTY(Class) \ { \ const FStructProperty* StructProperty = CastField(Property); \ OldValueVariant = *StructProperty->ContainerPtrToValuePtr(TargetObject); \ } #define SET_VARIANT_ENUM_PROPERTY() \ { \ const FEnumProperty* EnumProperty = CastField(Property); \ const FNumericProperty* NumProp = EnumProp->GetUnderlyingProperty(); \ const uint64 Value = NumProp->GetUnsignedIntPropertyValue(EnumProp->ContainerPtrToValuePtr(TargetObject)); \ OldValueVariant = static_cast( Value ); \ } #define CALL_PROPERTY_DISPATCHER(Type, PropertyClass, ValueType) \ { \ const PropertyClass* TypedProperty = CastField(Property); \ ValueType CurrentValue = TypedProperty->GetPropertyValue(ValuePtr); \ ValueType OldValue = OldValueVariant.GetValue(); \ if (CurrentValue != OldValue) \ { \ CallChangeProperty##Type##Dispatcher(PropertyName, TargetObject, CurrentValue, OldValue, PropertySaveKey); \ } \ } #define CALL_STRUCT_PROPERTY_DISPATCHER(Type, StructType) \ { \ const FStructProperty* StructProperty = CastField(Property); \ StructType CurrentValue = *StructProperty->ContainerPtrToValuePtr(TargetObject); \ StructType OldValue = OldValueVariant.GetValue(); \ if (CurrentValue != OldValue) \ { \ CallChangeProperty##Type##Dispatcher(PropertyName, TargetObject, CurrentValue, OldValue, PropertySaveKey); \ } \ } #define CALL_ENUM_PROPERTY_DISPATCHER() \ { \ const FEnumProperty* EnumProperty = CastField(Property); \ const FNumericProperty* NumProp = EnumProperty->GetUnderlyingProperty(); \ uint8 CurrentValue = static_cast(NumProp->GetUnsignedIntPropertyValue(ValuePtr)); \ uint8 OldValue = OldValueVariant.GetValue(); \ if (CurrentValue != OldValue) \ { \ CallChangePropertyByteDispatcher(PropertyName, TargetObject, CurrentValue, OldValue, PropertySaveKey); \ } \ } bool AGameDebugMenuManager::RegisterObjectProperty(UObject* TargetObject, const FName PropertyName, const FGDMGameplayCategoryKey& CategoryKey, const FString& PropertySaveKey, const FText& DisplayPropertyName, const FText& Description, const FGDMPropertyUIConfigInfo& PropertyUIConfigInfo, const int32& DisplayPriority) { if(!IsValid(TargetObject)) { UE_LOG(LogGDM, Warning, TEXT("RegisterObjectProperty: Not found TargetObject")); return false; } FProperty* Property = TargetObject->GetClass()->FindPropertyByName(PropertyName); if( Property == nullptr ) { UE_LOG(LogGDM, Warning, TEXT("RegisterObjectProperty: Not found Property")); return false; } const EGDMPropertyType PropertyType = GetPropertyType(Property); if(PropertyType == EGDMPropertyType::GDM_Null) { UE_LOG(LogGDM, Warning, TEXT("RegisterObjectProperty: not supported Property: %s: %s"), *PropertyName.ToString(), *GetNameSafe(Property)); return false; } const TSharedPtr PropertyInfo = MakeShareable(new FGDMObjectPropertyInfo); PropertyInfo->CategoryKey = CategoryKey; PropertyInfo->Name = (DisplayPropertyName.IsEmpty() != false) ? FText::FromName(PropertyName) : DisplayPropertyName; PropertyInfo->TargetObject = TargetObject; PropertyInfo->PropertyName = PropertyName; PropertyInfo->TargetProperty = Property; PropertyInfo->ConfigInfo = PropertyUIConfigInfo; PropertyInfo->Description = Description; PropertyInfo->DisplayPriority = DisplayPriority; PropertyInfo->PropertySaveKey = PropertySaveKey; /* Enumならセット */ const FEnumProperty* EnumProp = CastField(Property);/* C++定義だとこっち */ const FByteProperty* ByteProp = CastField(Property);/* BP定義だとこっちみたい… */ const FStructProperty* StructProp = CastField(Property); if(EnumProp != nullptr) { PropertyInfo->EnumType = EnumProp->GetEnum(); } else if(ByteProp != nullptr) { PropertyInfo->EnumType = ByteProp->Enum; } else if( StructProp != nullptr ) { PropertyInfo->Struct = StructProp->Struct; } ObjectProperties.Add(PropertyInfo); ObjectProperties.Sort([](const TSharedPtr& A,const TSharedPtr& B) { return A->DisplayPriority >= B->DisplayPriority; }); if (!PropertySaveKey.IsEmpty()) { /* プロパティ型に応じた値を一時的に保存 */ FVariant OldValueVariant; void* ValuePtr = Property->ContainerPtrToValuePtr(TargetObject); switch (PropertyType) { case EGDMPropertyType::GDM_Bool: { SET_VARIANT_PROPERTY(FBoolProperty) break; } case EGDMPropertyType::GDM_Int: { SET_VARIANT_PROPERTY(FIntProperty) break; } case EGDMPropertyType::GDM_Float: { SET_VARIANT_PROPERTY(FFloatProperty) break; } case EGDMPropertyType::GDM_Enum: { SET_VARIANT_ENUM_PROPERTY() break; } case EGDMPropertyType::GDM_Byte: { SET_VARIANT_PROPERTY(FByteProperty) break; } case EGDMPropertyType::GDM_String: { SET_VARIANT_PROPERTY(FStrProperty) break; } case EGDMPropertyType::GDM_Vector: { SET_VARIANT_STRUCT_PROPERTY(FVector) break; } case EGDMPropertyType::GDM_Vector2D:{ SET_VARIANT_STRUCT_PROPERTY(FVector2D) break; } case EGDMPropertyType::GDM_Rotator: { SET_VARIANT_STRUCT_PROPERTY(FRotator) break; } default: UE_LOG(LogGDM, Warning, TEXT("Unsupported property type for old value caching")); } /* 保存キーを指定してるため、既に一致する情報があればそれをプロパティにセットを試みる */ if (!GetPropertyJsonSystemComponent()->ApplyJsonToObjectProperty(PropertySaveKey, TargetObject, PropertyName.ToString())) { /* 失敗、データがないので現状の値をJsonに書き込み */ GetPropertyJsonSystemComponent()->AddPropertyToJson(PropertySaveKey, TargetObject, PropertyName.ToString()); } else { switch (PropertyType) { case EGDMPropertyType::GDM_Bool: { CALL_PROPERTY_DISPATCHER(Bool, FBoolProperty, bool) break; } case EGDMPropertyType::GDM_Int: { CALL_PROPERTY_DISPATCHER(Int, FIntProperty, int32) break; } case EGDMPropertyType::GDM_Float: { CALL_PROPERTY_DISPATCHER(Float, FFloatProperty, float) break; } case EGDMPropertyType::GDM_Enum: { CALL_ENUM_PROPERTY_DISPATCHER() break; } case EGDMPropertyType::GDM_Byte: { CALL_PROPERTY_DISPATCHER(Byte, FByteProperty, uint8) break; } case EGDMPropertyType::GDM_String: { CALL_PROPERTY_DISPATCHER(String, FStrProperty, FString) break; } case EGDMPropertyType::GDM_Vector: { CALL_STRUCT_PROPERTY_DISPATCHER(Vector, FVector); break; } case EGDMPropertyType::GDM_Vector2D:{ CALL_STRUCT_PROPERTY_DISPATCHER(Vector2D, FVector2D); break; } case EGDMPropertyType::GDM_Rotator: { CALL_STRUCT_PROPERTY_DISPATCHER(Rotator, FRotator); break; } default: UE_LOG(LogGDM, Warning, TEXT("Unsupported property type for old value caching")); } } } return true; } bool AGameDebugMenuManager::RegisterObjectFunction(UObject* TargetObject, const FName FunctionName, const FGDMGameplayCategoryKey& CategoryKey, const FString& FunctionSaveKey, const FText& DisplayFunctionName,const FText& Description,const int32& DisplayPriority) { if(!IsValid(TargetObject)) { UE_LOG(LogGDM, Warning, TEXT("RegisterObjectFunction: Not found TargetObject")); return false; } UFunction* Function = TargetObject->GetClass()->FindFunctionByName(FunctionName); if( Function == nullptr ) { UE_LOG(LogGDM, Warning, TEXT("RegisterObjectFunction: Not found Function")); return false; } if (Function->NumParms != 0) { UE_LOG(LogGDM, Warning, TEXT("RegisterObjectFunction: Arguments are not supported: %d"), Function->NumParms); return false; } const TSharedPtr FunctionInfo = MakeShareable(new FGDMObjectFunctionInfo); FunctionInfo->CategoryKey = CategoryKey; FunctionInfo->Name = (DisplayFunctionName.IsEmpty() != false) ? FText::FromName(FunctionName) : DisplayFunctionName; FunctionInfo->TargetObject = TargetObject; FunctionInfo->FunctionName = FunctionName; FunctionInfo->TargetFunction = Function; FunctionInfo->Description = Description; FunctionInfo->DisplayPriority = DisplayPriority; FunctionInfo->FunctionSaveKey = FunctionSaveKey; ObjectFunctions.Add(FunctionInfo); ObjectFunctions.Sort([](const TSharedPtr& A,const TSharedPtr& B) { return A->DisplayPriority >= B->DisplayPriority; }); if (!FunctionSaveKey.IsEmpty()) { /* 保存キーを指定してるため、既に一致する情報があればそれをプロパティにセットを試みる */ if (!GetPropertyJsonSystemComponent()->HaveFunctionInJson(FunctionSaveKey, TargetObject, FunctionName.ToString())) { /* 失敗、データがないので現状の値をJsonに書き込み */ GetPropertyJsonSystemComponent()->AddFunctionToJson(FunctionSaveKey, TargetObject, FunctionName.ToString()); } } return true; } UObject* AGameDebugMenuManager::GetObjectProperty(const int32 Index, FGDMGameplayCategoryKey& OutCategoryKey, FString& OutPropertySaveKey, FText& OutDisplayPropertyName, FText& OutDescription, FName& OutPropertyName, EGDMPropertyType& OutPropertyType, FString& OutEnumPathName, FGDMPropertyUIConfigInfo& OutPropertyUIConfigInfo) { OutPropertyType = EGDMPropertyType::GDM_Null; if(!ObjectProperties.IsValidIndex(Index)) { UE_LOG(LogGDM, Warning, TEXT("GetObjectProperty: Not found Index: %d"), Index); return nullptr; } const auto& ObjProp = ObjectProperties[Index]; if(!ObjProp->TargetObject.IsValid()) { UE_LOG(LogGDM, Warning, TEXT("GetObjectProperty: Not found TargetObject")); return nullptr; } const auto Property = ObjProp->TargetProperty; if(Property == nullptr) { UE_LOG(LogGDM, Warning, TEXT("GetObjectProperty: Not found TargetProperty")); return nullptr; } OutCategoryKey = ObjProp->CategoryKey; OutPropertySaveKey = ObjProp->PropertySaveKey; OutDisplayPropertyName = ObjProp->Name; OutDescription = ObjProp->Description; OutPropertyName = ObjProp->PropertyName; OutPropertyUIConfigInfo = ObjProp->ConfigInfo; OutPropertyType = GetPropertyType(Property); if(OutPropertyType == EGDMPropertyType::GDM_Enum) { OutEnumPathName = ObjProp->EnumType->GetPathName(); } else if(OutPropertyType == EGDMPropertyType::GDM_Byte) { if(ObjProp->EnumType.IsValid()) { /* Enumならセット */ OutPropertyType = EGDMPropertyType::GDM_Enum; OutEnumPathName = ObjProp->EnumType->GetPathName(); } } return ObjProp->TargetObject.Get(); } void AGameDebugMenuManager::RemoveObjectProperty(const int32 Index) { ObjectProperties.RemoveAt(Index); } UObject* AGameDebugMenuManager::GetObjectFunction(const int32 Index, FGDMGameplayCategoryKey& OutCategoryKey, FString& OutFunctionSaveKey, FText& OutDisplayFunctionName, FText& OutDescription, FName& OutFunctionName) { if(!ObjectFunctions.IsValidIndex(Index)) { UE_LOG(LogGDM, Warning, TEXT("GetObjectFunction: Not found Index: %d"),Index); return nullptr; } const auto& ObjFunc = ObjectFunctions[Index]; if(!ObjFunc->TargetObject.IsValid()) { UE_LOG(LogGDM, Warning, TEXT("GetObjectFunction: Not found TargetObject")); return nullptr; } const auto& Function = ObjFunc->TargetFunction; if(!Function.IsValid()) { UE_LOG(LogGDM, Warning, TEXT("GetObjectFunction: Not found TargetFunction")); return nullptr; } OutCategoryKey = ObjFunc->CategoryKey; OutDisplayFunctionName = ObjFunc->Name; OutDescription = ObjFunc->Description; OutFunctionName = ObjFunc->FunctionName; OutFunctionSaveKey = ObjFunc->FunctionSaveKey; return ObjFunc->TargetObject.Get(); } void AGameDebugMenuManager::RemoveObjectFunction(const int32 Index) { ObjectFunctions.RemoveAt(Index); } int32 AGameDebugMenuManager::GetNumObjectProperties() const { return ObjectProperties.Num(); } int32 AGameDebugMenuManager::GetNumObjectFunctions() const { return ObjectFunctions.Num(); } UObject* AGameDebugMenuManager::TryGetObjectProperty(const FString& InPropertySaveKey, const FString& InPropertyName, FGDMGameplayCategoryKey& OutCategoryKey, FText& OutDisplayPropertyName, FText& OutDescription, EGDMPropertyType& OutPropertyType, FString& OutEnumPathName, FGDMPropertyUIConfigInfo& OutPropertyUIConfigInfo) const { OutPropertyType = EGDMPropertyType::GDM_Null; for (const auto& ObjProp : ObjectProperties) { if(!ObjProp->TargetObject.IsValid()) { continue; } const auto Property = ObjProp->TargetProperty; if(Property == nullptr) { continue; } if (ObjProp->PropertySaveKey != InPropertySaveKey || ObjProp->PropertyName != InPropertyName) { continue; } OutCategoryKey = ObjProp->CategoryKey; OutDisplayPropertyName = ObjProp->Name; OutDescription = ObjProp->Description; OutPropertyUIConfigInfo = ObjProp->ConfigInfo; OutPropertyType = GetPropertyType(Property); if(OutPropertyType == EGDMPropertyType::GDM_Enum) { OutEnumPathName = ObjProp->EnumType->GetPathName(); } else if(OutPropertyType == EGDMPropertyType::GDM_Byte) { if(ObjProp->EnumType.IsValid()) { /* Enumならセット */ OutPropertyType = EGDMPropertyType::GDM_Enum; OutEnumPathName = ObjProp->EnumType->GetPathName(); } } return ObjProp->TargetObject.Get(); } return nullptr; } UObject* AGameDebugMenuManager::TryGetObjectFunction(const FString& InFunctionSaveKey, const FString& InFunctionName, FGDMGameplayCategoryKey& OutCategoryKey, FText& OutDisplayFunctionName, FText& OutDescription) const { for (const auto& ObjFunc : ObjectFunctions) { if(!ObjFunc->TargetObject.IsValid()) { continue; } const auto& Function = ObjFunc->TargetFunction; if(!Function.IsValid()) { continue; } if (ObjFunc->FunctionSaveKey != InFunctionSaveKey || ObjFunc->FunctionName != InFunctionName) { continue; } OutCategoryKey = ObjFunc->CategoryKey; OutDisplayFunctionName = ObjFunc->Name; OutDescription = ObjFunc->Description; return ObjFunc->TargetObject.Get(); } return nullptr; } void AGameDebugMenuManager::AddDebugMenuPCProxyComponent(APlayerController* PlayerController) { check(IsValid(MenuAsset)); if (!IsValid(MenuAsset->DebugMenuPCProxyComponentClass)) { UE_LOG(LogGDM, Warning, TEXT("AddDebugMenuPCProxyComponent: Not found DebugMenuPCProxyComponentClass")); return; } UGDMPlayerControllerProxyComponent* NewComponent = Cast( PlayerController->AddComponentByClass(MenuAsset->DebugMenuPCProxyComponentClass, false, FTransform::Identity, true)); check(IsValid(NewComponent)); NewComponent->DebugMenuManager = this; PlayerController->FinishAddComponent(NewComponent, false, FTransform::Identity); } void AGameDebugMenuManager::SetIgnoreInput(bool bNewInput) { GetDebugMenuInputSystemComponent()->SetIgnoreInput(bNewInput); } void AGameDebugMenuManager::ResetIgnoreInput() { GetDebugMenuInputSystemComponent()->ResetIgnoreInput(); } bool AGameDebugMenuManager::IsInputIgnored() const { return GetDebugMenuInputSystemComponent()->IsInputIgnored(); } void AGameDebugMenuManager::ChangeDebugMenuLanguage(FName LanguageKey, bool bForcedUpdate) { UE_LOG(LogGDM, Verbose, TEXT("Call ChangeDebugMenuLanguage LanguageKey:%s bForcedUpdate:%d"), *LanguageKey.ToString(), bForcedUpdate); const FName CurrentDebugMenuLanguage = GetLocalizeStringComponent()->GetCurrentDebugMenuLanguage(); if( !bForcedUpdate ) { if( LanguageKey == NAME_None ) { return; } if( CurrentDebugMenuLanguage == LanguageKey ) { return; } } const FName Old = CurrentDebugMenuLanguage; /* 言語を切り替えをして保存。その後テーブルを読み直す */ { GetLocalizeStringComponent()->SetToJsonSystemComponent(GetPropertyJsonSystemComponent(), LanguageKey.ToString()); GetSaveSystemComponent()->SaveDebugMenuFile(); GetLocalizeStringComponent()->SyncLoadDebugMenuStringTables(); } TArray ChildWidgets; for(const auto& ViewportWidget : ViewportDebugMenuWidgets ) { /* Viewportに追加 Widget 内にあるすべてのTextBlockとDebugMenuWidgetを更新 */ { ViewportWidget->GetWidgetChildrenOfClass(UGDMTextBlock::StaticClass(), ChildWidgets, false); for(const auto ChildWidget : ChildWidgets ) { if( UGDMTextBlock* TextBlock = Cast(ChildWidget) ) { if( !TextBlock->DebugMenuStringKey.IsEmpty() ) { TextBlock->SetText(FText::FromString(TextBlock->DebugMenuStringKey)); } } } ViewportWidget->GetWidgetChildrenOfClass(UGameDebugMenuWidget::StaticClass(), ChildWidgets, false); for(const auto ChildWidget : ChildWidgets ) { if( UGameDebugMenuWidget* DebugMenuWidget = Cast(ChildWidget) ) { DebugMenuWidget->OnChangeDebugMenuLanguage(LanguageKey, Old); } } } ViewportWidget->OnChangeDebugMenuLanguage(LanguageKey, Old); } CallChangeDebugMenuLanguageDispatcher(LanguageKey, Old); } TArray AGameDebugMenuManager::GetViewportDebugMenuWidgets() { return ViewportDebugMenuWidgets; } void AGameDebugMenuManager::ChangeConsoleCommandSetAsset(UGDMConsoleCommandSetAsset* NewCommandSetAsset) { ConsoleCommandSetAsset = NewCommandSetAsset; } UGDMConsoleCommandSetAsset* AGameDebugMenuManager::GetConsoleCommandSetAsset() const { return ConsoleCommandSetAsset; } void AGameDebugMenuManager::CallExecuteConsoleCommandDispatcher(const FString& Command) { TArray ListenerComponents; UGDMListenerComponent::GetAllListenerComponents(GetWorld(), ListenerComponents); for(const auto& Component : ListenerComponents) { Component->OnExecuteConsoleCommandDispatcher.Broadcast(Command); } } void AGameDebugMenuManager::CallShowDispatcher() { TArray ListenerComponents; UGDMListenerComponent::GetAllListenerComponents(GetWorld(), ListenerComponents); for(const auto& Component : ListenerComponents) { Component->OnShowDispatcher.Broadcast(); } } void AGameDebugMenuManager::CallHideDispatcher() { TArray ListenerComponents; UGDMListenerComponent::GetAllListenerComponents(GetWorld(), ListenerComponents); for(const auto& Component : ListenerComponents) { Component->OnHideDispatcher.Broadcast(); } } void AGameDebugMenuManager::OnWidgetAdded(UWidget* AddWidget, ULocalPlayer* Player) { if(UGameDebugMenuWidget* W = Cast(AddWidget)) { /* このManagerのOwner LocalPlayerに紐づくWidgetだけを対象にする */ const APlayerController* OwnerPC = Cast(GetOwner()); const ULocalPlayer* OwnerLocalPlayer = OwnerPC ? OwnerPC->GetLocalPlayer() : nullptr; if (IsValid(OwnerLocalPlayer)) { if (IsValid(Player) && Player != OwnerLocalPlayer) { return; } if (W->GetOwningLocalPlayer() != OwnerLocalPlayer) { return; } } if (W->GetWorld() != GetWorld()) { return; } ViewportDebugMenuWidgets.AddUnique(W); } } void AGameDebugMenuManager::OnWidgetRemoved(UWidget* RemoveWidget) { if(UGameDebugMenuWidget* W = Cast(RemoveWidget)) { const APlayerController* OwnerPC = Cast(GetOwner()); const ULocalPlayer* OwnerLocalPlayer = OwnerPC ? OwnerPC->GetLocalPlayer() : nullptr; if (IsValid(OwnerLocalPlayer) && W->GetOwningLocalPlayer() != OwnerLocalPlayer) { return; } if (W->GetWorld() != GetWorld()) { return; } ViewportDebugMenuWidgets.RemoveSingle(W); } } void AGameDebugMenuManager::CallExecuteProcessEventDispatcher(const FName& FunctionName, UObject* TargetObject) { TArray ListenerComponents; UGDMListenerComponent::GetAllListenerComponents(GetWorld(), ListenerComponents); for(const auto& Component : ListenerComponents) { Component->OnExecuteProcessEventDispatcher.Broadcast(FunctionName, TargetObject); } } void AGameDebugMenuManager::CallChangePropertyBoolDispatcher(const FName& PropertyName, UObject* PropertyOwnerObject, bool New, bool Old, const FString& PropertySaveKey) { TArray ListenerComponents; UGDMListenerComponent::GetAllListenerComponents(GetWorld(), ListenerComponents); for(const auto& Component : ListenerComponents) { Component->OnChangePropertyBoolDispatcher.Broadcast(PropertyName, PropertyOwnerObject, New, Old, PropertySaveKey); } } void AGameDebugMenuManager::CallChangePropertyIntDispatcher(const FName& PropertyName, UObject* PropertyOwnerObject, int32 New, int32 Old, const FString& PropertySaveKey) { TArray ListenerComponents; UGDMListenerComponent::GetAllListenerComponents(GetWorld(), ListenerComponents); for(const auto& Component : ListenerComponents) { Component->OnChangePropertyIntDispatcher.Broadcast(PropertyName, PropertyOwnerObject, New, Old, PropertySaveKey); } } void AGameDebugMenuManager::CallChangePropertyFloatDispatcher(const FName& PropertyName, UObject* PropertyOwnerObject, float New, float Old, const FString& PropertySaveKey) { TArray ListenerComponents; UGDMListenerComponent::GetAllListenerComponents(GetWorld(), ListenerComponents); for(const auto& Component : ListenerComponents) { Component->OnChangePropertyFloatDispatcher.Broadcast(PropertyName, PropertyOwnerObject, New, Old, PropertySaveKey); } } void AGameDebugMenuManager::CallChangePropertyByteDispatcher(const FName& PropertyName, UObject* PropertyOwnerObject, uint8 New, uint8 Old, const FString& PropertySaveKey) { TArray ListenerComponents; UGDMListenerComponent::GetAllListenerComponents(GetWorld(), ListenerComponents); for(const auto& Component : ListenerComponents) { Component->OnChangePropertyByteDispatcher.Broadcast(PropertyName, PropertyOwnerObject, New, Old, PropertySaveKey); } } void AGameDebugMenuManager::CallChangePropertyStringDispatcher(const FName& PropertyName, UObject* PropertyOwnerObject, FString New, FString Old, const FString& PropertySaveKey) { TArray ListenerComponents; UGDMListenerComponent::GetAllListenerComponents(GetWorld(), ListenerComponents); for(const auto& Component : ListenerComponents) { Component->OnChangePropertyStringDispatcher.Broadcast(PropertyName, PropertyOwnerObject, New, Old, PropertySaveKey); } } void AGameDebugMenuManager::CallChangePropertyVectorDispatcher(const FName& PropertyName, UObject* PropertyOwnerObject, FVector New, FVector Old, const FString& PropertySaveKey) { TArray ListenerComponents; UGDMListenerComponent::GetAllListenerComponents(GetWorld(), ListenerComponents); for(const auto& Component : ListenerComponents ) { Component->OnChangePropertyVectorDispatcher.Broadcast(PropertyName, PropertyOwnerObject, New, Old, PropertySaveKey); } } void AGameDebugMenuManager::CallChangePropertyVector2DDispatcher(const FName& PropertyName, UObject* PropertyOwnerObject, FVector2D New, FVector2D Old, const FString& PropertySaveKey) { TArray ListenerComponents; UGDMListenerComponent::GetAllListenerComponents(GetWorld(), ListenerComponents); for(const auto& Component : ListenerComponents ) { Component->OnChangePropertyVector2DDispatcher.Broadcast(PropertyName, PropertyOwnerObject, New, Old, PropertySaveKey); } } void AGameDebugMenuManager::CallChangePropertyRotatorDispatcher(const FName& PropertyName, UObject* PropertyOwnerObject, FRotator New, FRotator Old, const FString& PropertySaveKey) { TArray ListenerComponents; UGDMListenerComponent::GetAllListenerComponents(GetWorld(), ListenerComponents); for(const auto& Component : ListenerComponents ) { Component->OnChangePropertyRotatorDispatcher.Broadcast(PropertyName, PropertyOwnerObject, New, Old, PropertySaveKey); } } void AGameDebugMenuManager::CallChangeDebugMenuLanguageDispatcher(const FName& NewLanguageKey, const FName& OldLanguageKey) { TArray ListenerComponents; UGDMListenerComponent::GetAllListenerComponents(GetWorld(), ListenerComponents); for(const auto& Component : ListenerComponents ) { Component->OnChangeDebugMenuLanguageDispatcher.Broadcast(NewLanguageKey, OldLanguageKey); } } void AGameDebugMenuManager::CallStartScreenshotRequestDispatcher() { TArray ListenerComponents; UGDMListenerComponent::GetAllListenerComponents(GetWorld(), ListenerComponents); for(const auto& Component : ListenerComponents ) { Component->OnStartScreenshotRequestDispatcher.Broadcast(); } } void AGameDebugMenuManager::CallScreenshotRequestProcessedDispatcher() { TArray ListenerComponents; UGDMListenerComponent::GetAllListenerComponents(GetWorld(), ListenerComponents); for(const auto& Component : ListenerComponents ) { Component->OnScreenshotRequestProcessedDispatcher.Broadcast(); } } void AGameDebugMenuManager::CallLoadedDebugMenuDispatcher() { TArray ListenerComponents; UGDMListenerComponent::GetAllListenerComponents(GetWorld(), ListenerComponents); for(const auto& Component : ListenerComponents ) { Component->OnLoadedDebugMenuDispatcher.Broadcast(); } } void AGameDebugMenuManager::CallSavedDebugMenuDispatcher() { TArray ListenerComponents; UGDMListenerComponent::GetAllListenerComponents(GetWorld(), ListenerComponents); for(const auto& Component : ListenerComponents ) { Component->OnSavedDebugMenuDispatcher.Broadcast(); } } void AGameDebugMenuManager::CallDeletedDebugMenuDispatcher() { TArray ListenerComponents; UGDMListenerComponent::GetAllListenerComponents(GetWorld(), ListenerComponents); for(const auto& Component : ListenerComponents ) { Component->OnDeletedDebugMenuDispatcher.Broadcast(); } } ``` `Plugin/GameDebugMenu/Source/GameDebugMenu/Private/GameDebugMenuSettings.cpp`: ```cpp /** * Copyright (c) 2020 akihiko moroi * * This software is released under the MIT License. * (See accompanying file LICENSE.txt or copy at http://opensource.org/licenses/MIT) */ #include "GameDebugMenuSettings.h" #include "Performance/EnginePerformanceTargets.h" #include #include "GameDebugMenuTypes.h" #include "Components/Widget.h" #include "Data/GameDebugMenuMasterAsset.h" #include "Engine/AssetManager.h" #include "Input/GDMEnhancedInputComponent.h" #include "Reports/GDMRequesterJira.h" #include "Reports/GDMRequesterRedmine.h" #include "Reports/GDMRequesterTrello.h" UGameDebugMenuSettings::UGameDebugMenuSettings() { CategoryName = TEXT("Plugins"); SetupCategoryResets(); SetupCategorySlomo(); SetupCategoryCamera(); SetupCategoryProfiler(); SetupCategoryDisplay(); SetupCategoryShowDebug(); SetupCategoryViewMode(); SetupCategoryScalability(); SetupCategoryFreeze(); SetupCategoryDumpLogs(); SetupCategoryNetwork(); SetupCategorySound(); SetupCategoryAbilitySystem(); SetupCategoryOther(); SetupCategoryLogVerbosity(); MasterAssetName = TEXT("DA_GDM_Master"); OrderConsoleCommandCategoryTitles.Add(FGDMOrderMenuCategoryTitle(TEXT("Resets"),0)); OrderConsoleCommandCategoryTitles.Add(FGDMOrderMenuCategoryTitle(TEXT("Slomo"),1)); OrderConsoleCommandCategoryTitles.Add(FGDMOrderMenuCategoryTitle(TEXT("Camera"),2)); OrderConsoleCommandCategoryTitles.Add(FGDMOrderMenuCategoryTitle(TEXT("Profiler"),3)); OrderConsoleCommandCategoryTitles.Add(FGDMOrderMenuCategoryTitle(TEXT("Display"),4)); OrderConsoleCommandCategoryTitles.Add(FGDMOrderMenuCategoryTitle(TEXT("Show Debug"),5)); OrderConsoleCommandCategoryTitles.Add(FGDMOrderMenuCategoryTitle(TEXT("ViewMode"),6)); OrderConsoleCommandCategoryTitles.Add(FGDMOrderMenuCategoryTitle(TEXT("Scalability"),7)); OrderConsoleCommandCategoryTitles.Add(FGDMOrderMenuCategoryTitle(TEXT("Freeze"),8)); OrderConsoleCommandCategoryTitles.Add(FGDMOrderMenuCategoryTitle(TEXT("Dump Logs"),9)); OrderConsoleCommandCategoryTitles.Add(FGDMOrderMenuCategoryTitle(TEXT("Network"),10)); OrderConsoleCommandCategoryTitles.Add(FGDMOrderMenuCategoryTitle(TEXT("Sounds"),11)); OrderConsoleCommandCategoryTitles.Add(FGDMOrderMenuCategoryTitle(TEXT("Ability System"),12)); OrderConsoleCommandCategoryTitles.Add(FGDMOrderMenuCategoryTitle(TEXT("Other"),13)); OrderConsoleCommandCategoryTitles.Add(FGDMOrderMenuCategoryTitle(TEXT("Log Verbosity"),14)); OrderGameplayCategoryTitles.Add(FGDMOrderMenuCategoryTitle(TEXT("Other"),0)); /* AGameDebugMenuManagerもデフォルトではInt最大値なのでそれより低くする * 同じ、または大きくした場合、マネージャーで設定する入力はメニューが閉じられるまで反応しなくなるので注意 */ WidgetInputActionPriority = TNumericLimits::Max() - 1; CultureList.Add(TEXT("ja")); CultureList.Add(TEXT("en")); DefaultGameDebugMenuLanguage = TEXT("Japanese"); bDisableScreenCaptureProcessingWhenOpeningDebugMenu = true; bUseSaveGame = false; SaveFilePath = TEXT("Saved/DebugMenu"); SaveFileName = TEXT("DebugMenuSaveData"); bDisableSaveFile = false; bDoesNotSaveConsoleCommand = false; MaxCommandHistoryNum = 100; NoSaveConsoleCommands.Reset(); NoSaveConsoleCommands.Add(TEXT("LevelEditor.")); NoSaveConsoleCommands.Add(TEXT("ToggleDebugCamera")); NoSaveConsoleCommands.Add(TEXT("stat ")); NoSaveConsoleCommands.Add(TEXT("LoadTimes.")); NoSaveConsoleCommands.Add(TEXT("CsvProfile ")); NoSaveConsoleCommands.Add(TEXT("Obj ")); NoSaveConsoleCommands.Add(TEXT("Freeze")); LineBreakString = TEXT("\n"); MasterAsset = nullptr; } #if WITH_EDITOR void UGameDebugMenuSettings::PostEditChangeProperty(struct FPropertyChangedEvent& PropertyChangedEvent) { Super::PostEditChangeProperty(PropertyChangedEvent); int32 Count = INDEX_NONE; for( auto& CategoryTitle : OrderConsoleCommandCategoryTitles ) { CategoryTitle.PreviewTitle = GetDebugMenuString(DefaultGameDebugMenuLanguage, CategoryTitle.Title); if( CategoryTitle.Index > Count ) { Count = CategoryTitle.Index; } } for( auto& CategoryTitle : OrderConsoleCommandCategoryTitles ) { if( CategoryTitle.Index == INDEX_NONE ) { ++Count; CategoryTitle.Index = Count; } } Count = INDEX_NONE; for( auto& CategoryTitle : OrderGameplayCategoryTitles ) { CategoryTitle.PreviewTitle = GetDebugMenuString(DefaultGameDebugMenuLanguage, CategoryTitle.Title); if( CategoryTitle.Index > Count ) { Count = CategoryTitle.Index; } } for( auto& CategoryTitle : OrderGameplayCategoryTitles ) { if( CategoryTitle.Index == INDEX_NONE ) { ++Count; CategoryTitle.Index = Count; } } } FText UGameDebugMenuSettings::GetSectionText() const { return FText::FromString(TEXT("Game Debug Menu")); } #endif TArray UGameDebugMenuSettings::GetIssueCategoryNameList() const { switch (ProjectManagementToolType) { case EGDMProjectManagementTool::Trello: { return TrelloSettings.CardListNames; } case EGDMProjectManagementTool::Redmine: { return RedmineSettings.TrackerNameList; } case EGDMProjectManagementTool::Jira: { return JiraSettings.IssueTypeList; } } TArray Empty; return Empty; } TArray UGameDebugMenuSettings::GetPriorityNameList() const { switch (ProjectManagementToolType) { case EGDMProjectManagementTool::Trello: { break;/* 特になし */ } case EGDMProjectManagementTool::Redmine: { return RedmineSettings.PriorityNameList; } case EGDMProjectManagementTool::Jira: { return JiraSettings.PriorityNameList; } } TArray Empty; return Empty; } TArray UGameDebugMenuSettings::GetAssigneeNameList() const { switch(ProjectManagementToolType) { case EGDMProjectManagementTool::Trello: { /* 未対応 */ break; } case EGDMProjectManagementTool::Redmine: { /* 未対応 */ break; } case EGDMProjectManagementTool::Jira: { TArray TextList; JiraSettings.AssigneeList.GenerateValueArray(TextList); return TextList; } } TArray Empty; return Empty; } int32 UGameDebugMenuSettings::GetDefaultIssueCategoryIndex() const { switch (ProjectManagementToolType) { case EGDMProjectManagementTool::Trello: { break;/* 特になし */ } case EGDMProjectManagementTool::Redmine: { return RedmineSettings.DefaultTrackerIndex; } case EGDMProjectManagementTool::Jira: { return JiraSettings.DefaultIssueTypeIndex; } } return 0; } int32 UGameDebugMenuSettings::GetDefaultPriorityIndex() const { switch (ProjectManagementToolType) { case EGDMProjectManagementTool::Trello: { break;/* 特になし */ } case EGDMProjectManagementTool::Redmine: { return RedmineSettings.DefaultPriorityIndex; } case EGDMProjectManagementTool::Jira: { return JiraSettings.DefaultPriorityIndex; } } return 0; } FString UGameDebugMenuSettings::GetGameplayCategoryTitle(const int32& ArrayIndex) const { FString ReturnValue; if( OrderGameplayCategoryTitles.IsValidIndex(ArrayIndex) ) { const FString& Title = OrderGameplayCategoryTitles[ArrayIndex].Title; ReturnValue = GetDebugMenuString(DefaultGameDebugMenuLanguage, Title); if( ReturnValue.IsEmpty() ) { ReturnValue = Title; } } return ReturnValue; } int32 UGameDebugMenuSettings::GetGameplayCategoryIndex(const int32& ArrayIndex) const { if( OrderGameplayCategoryTitles.IsValidIndex(ArrayIndex) ) { return OrderGameplayCategoryTitles[ArrayIndex].Index; } return 0; } FString UGameDebugMenuSettings::GetFullSavePath() const { return FPaths::ProjectDir().Append(SaveFilePath).Append(TEXT("/")).Append(SaveFileName).Append(TEXT(".json")); } const FGDMStringTableList* UGameDebugMenuSettings::TryGetStringTableList(const FName& LanguageKey) const { if (const auto Master = GetMasterAsset()) { return Master->GameDebugMenuStringTables.Find(LanguageKey); } return nullptr; } TArray UGameDebugMenuSettings::GetDebugMenuLanguageKeys() const { TArray ReturnValues; if (const auto Master = GetMasterAsset()) { Master->GameDebugMenuStringTables.GetKeys(ReturnValues); } return ReturnValues; } UClass* UGameDebugMenuSettings::GetDebugMenuInputComponentClass() const { TSoftClassPtr Class = nullptr; if (const auto Master = GetMasterAsset()) { Class = Master->DebugMenuInputComponentClass; } ensureMsgf(Class.IsValid(), TEXT("Invalid DebugMenuInputComponentClass class in GameDebugMenuSettings. Manual reset required.")); return Class.IsValid() ? Class.Get() : UGDMEnhancedInputComponent::StaticClass(); } const TSubclassOf* UGameDebugMenuSettings::GetDebugReportRequesterClass() const { if (const auto Master = GetMasterAsset()) { return Master->DebugReportRequesterClass.Find(ProjectManagementToolType); } return nullptr; } FString UGameDebugMenuSettings::GetDebugMenuString(const FName& LanguageKey, const FString& StringKey) const { if( const FGDMStringTableList* StringTableList = TryGetStringTableList(LanguageKey) ) { FString OutSourceString; for( auto& StrTablePtr : StringTableList->StringTables ) { if( !StrTablePtr.ToSoftObjectPath().IsValid() || StrTablePtr.ToSoftObjectPath().IsNull() ) { UE_LOG(LogGDM, Warning, TEXT("GetDebugMenuString: failed StringTable : LanguageKey->%s StringKey->%s"), *LanguageKey.ToString(), *StringKey); continue; } if(const UStringTable* StringTable = StrTablePtr.LoadSynchronous() ) { const FStringTableConstPtr TableData = StringTable->GetStringTable(); if (!TableData.IsValid() || !TableData->IsLoaded()) { UE_LOG(LogGDM, Warning, TEXT("GetDebugMenuString: StringTable not loaded: %s"), *StringTable->GetName()); continue; } if( TableData->GetSourceString(StringKey, OutSourceString) ) { return OutSourceString; } } } } return FString(); } UObject* UGameDebugMenuSettings::GetDebugMenuFont() const { if (const auto Master = GetMasterAsset()) { if (UObject* FontObj = Master->FontName.ResolveObject()) { return FontObj; } return Master->FontName.TryLoad(); } const FSoftObjectPath EngineFontPath = UWidget::GetDefaultFontName(); return EngineFontPath.TryLoad(); } UGameDebugMenuMasterAsset* UGameDebugMenuSettings::GetMasterAsset() const { const UAssetManager* AssetManager = UAssetManager::GetIfInitialized(); if (!IsValid(AssetManager)) { return nullptr; } if (MasterAssetName.IsEmpty()) { UE_LOG(LogGDM, Warning, TEXT("MasterAssetName is empty.")); return nullptr; } const FPrimaryAssetId MasterAssetId(UGameDebugMenuMasterAsset::GetPrimaryType(), FName(*MasterAssetName)); if (IsValid(MasterAsset)) { const FPrimaryAssetId CachedId = MasterAsset->GetPrimaryAssetId(); if (CachedId == MasterAssetId) { return MasterAsset.Get(); } } UObject* Asset = AssetManager->GetPrimaryAssetObject(MasterAssetId); if (!IsValid(Asset)) { const FSoftObjectPath AssetPath = AssetManager->GetPrimaryAssetPath(MasterAssetId); Asset = AssetPath.TryLoad(); } MasterAsset = Cast(Asset); return MasterAsset; } void UGameDebugMenuSettings::SetupCategoryResets() { FGDMConsoleCommandSingle Single; Single.CategoryIndex = 0; /* Resets */ Single.CommandNetType = EGDMConsoleCommandNetType::LocalOnly; Single.Title = FText::FromString(TEXT("Stat None")); Single.Description = FText::FromString(TEXT("Statをクリアする")); Single.ConsoleCommandName = TEXT("stat None"); ConsoleCommandNames.Add(Single); Single.Title = FText::FromString(TEXT("Grouped")); Single.Description = FText::FromString(TEXT("stat Slow を無効")); Single.ConsoleCommandName = TEXT("stat Grouped"); ConsoleCommandNames.Add(Single); Single.Title = FText::FromString(TEXT("Load Times Report Reset")); Single.Description = FText::FromString(TEXT("loadtimes.dumpreportの出力情報をリセットする")); Single.ConsoleCommandName = TEXT("LoadTimes.reset"); ConsoleCommandNames.Add(Single); FGDMConsoleCommandPair Pair; Pair.CategoryIndex = 0; /* Resets */ Pair.CommandNetType = EGDMConsoleCommandNetType::LocalOnly; Pair.Title = FText::FromString(TEXT("Toggle All Screen Messages")); Pair.Description = FText::FromString(TEXT("トグルで画面ログの(非)表示を切り替える")); Pair.ClickedEvent = EGDMConsoleCommandClickedEvent::Non; Pair.FirstConsoleCommandName = TEXT("DisableAllScreenMessages"); Pair.SecondConsoleCommandName = TEXT("EnableAllScreenMessages"); ConsoleCommandPairs.Add(Pair); } void UGameDebugMenuSettings::SetupCategorySlomo() { FGDMConsoleCommandSingle Single; Single.CategoryIndex = 1; /* Slomo */ Single.Title = FText::FromString(TEXT("Reset")); Single.Description = FText::FromString(TEXT("Slomoを1にリセットする")); Single.ConsoleCommandName = TEXT("Slomo 1"); Single.CommandNetType = EGDMConsoleCommandNetType::ServerAll; ConsoleCommandNames.Add(Single); FGDMConsoleCommandNumber Number; Number.CategoryIndex = 1; /* Slomo */ Number.PreConsoleCommandName = TEXT(""); Number.PostConsoleCommandName = TEXT(" "); Number.Title = FText::FromString(TEXT("Change Slomo")); Number.Description = FText::FromString(TEXT("Slomoを変更し反映させる")); Number.ConsoleCommandName = TEXT("Slomo"); Number.ConsoleVariableName = TEXT("Slomo"); Number.UIConfigInfo.Range.bUseMax = true; Number.UIConfigInfo.Range.bUseMin = true; Number.UIConfigInfo.Range.MinValue = 0.0f; Number.UIConfigInfo.Range.MaxValue = 10.0f; Number.UIConfigInfo.DefaultChangeAmount = 0.1f; Number.UIConfigInfo.MaxChangeAmount = 0.2f; Number.DefaultValue = 1.0f; Number.CommandNetType = EGDMConsoleCommandNetType::ServerAll; ConsoleCommandNumbers.Add(Number); } void UGameDebugMenuSettings::SetupCategoryCamera() { FGDMConsoleCommandSingle Single; Single.CategoryIndex = 2; /* Camera */ Single.CommandNetType = EGDMConsoleCommandNetType::LocalOnly; Single.Title = FText::FromString(TEXT("Debug Camera")); Single.Description = FText::FromString(TEXT("ゲーム中のカメラを離れ、デバッグ用の別カメラに切り替える\n同時に注視点のアセットの情報を画面に表示します")); Single.ClickedEvent = EGDMConsoleCommandClickedEvent::MenuClose; Single.ConsoleCommandName = TEXT("ToggleDebugCamera"); ConsoleCommandNames.Add(Single); Single.CommandNetType = EGDMConsoleCommandNetType::ServerAll; Single.Title = FText::FromString(TEXT("Teleport Player controlled pawn")); Single.Description = FText::FromString(TEXT("プレイヤー操作ポーンをカメラの注視点にテレポートさせる")); Single.ClickedEvent = EGDMConsoleCommandClickedEvent::Non; Single.ConsoleCommandName = TEXT("Teleport"); ConsoleCommandNames.Add(Single); FGDMConsoleCommandPair Pair; Pair.CategoryIndex = 2; /* Camera */ Pair.CommandNetType = EGDMConsoleCommandNetType::LocalOnly; Pair.Title = FText::FromString(TEXT("Debug Camera Trace Complex")); Pair.Description = FText::FromString(TEXT("")); Pair.ClickedEvent = EGDMConsoleCommandClickedEvent::Non; Pair.FirstConsoleCommandName = TEXT("g.DebugCameraTraceComplex 0"); Pair.SecondConsoleCommandName = TEXT("g.DebugCameraTraceComplex 1"); ConsoleCommandPairs.Add(Pair); } void UGameDebugMenuSettings::SetupCategoryProfiler() { FGDMConsoleCommandSingle Single; Single.CategoryIndex = 3; /* Profiler */ Single.CommandNetType = EGDMConsoleCommandNetType::LocalOnly; Single.Title = FText::FromString(TEXT("FPS")); Single.Description = FText::FromString(TEXT("")); Single.ConsoleCommandName = TEXT("stat fps"); ConsoleCommandNames.Add(Single); Single.Title = FText::FromString(TEXT("Unit")); Single.Description = FText::FromString(TEXT("")); Single.ConsoleCommandName = TEXT("stat Unit"); ConsoleCommandNames.Add(Single); Single.Title = FText::FromString(TEXT("Unit Graph")); Single.Description = FText::FromString(TEXT("")); Single.ConsoleCommandName = TEXT("stat UnitGraph"); ConsoleCommandNames.Add(Single); Single.Title = FText::FromString(TEXT("Unit Max")); Single.Description = FText::FromString(TEXT("")); Single.ConsoleCommandName = TEXT("stat UnitMax"); ConsoleCommandNames.Add(Single); Single.Title = FText::FromString(TEXT("Unit Time")); Single.Description = FText::FromString(TEXT("")); Single.ConsoleCommandName = TEXT("stat UnitTime"); ConsoleCommandNames.Add(Single); Single.Title = FText::FromString(TEXT("Raw")); Single.Description = FText::FromString(TEXT("")); Single.ConsoleCommandName = TEXT("stat Raw"); ConsoleCommandNames.Add(Single); FGDMConsoleCommandGroup Group; Group.CategoryIndex = 3; /* Profiler */ Group.CommandNetType = EGDMConsoleCommandNetType::LocalOnly; /* EditorOnly */ Group.Title = FText::FromString(TEXT("ProfileGPU ShowUI")); Group.Description = FText::FromString(TEXT("GPUの処理負荷を階層的に出力(エディター用UI表示版)\nエディターでのみ有効")); Group.ConsoleCommandNames.Add(TEXT("r.ProfileGPU.ShowUI 1")); Group.ConsoleCommandNames.Add(TEXT("ProfileGPU")); EditorOnlyConsoleCommandGroups.Add(Group); Group.ConsoleCommandNames.Empty(); /* EditorOnly */ Group.Title = FText::FromString(TEXT("ProfileGPU")); Group.Description = FText::FromString(TEXT("GPUの処理負荷を階層的に出力")); Group.ConsoleCommandNames.Add(TEXT("r.ProfileGPU.ShowUI 0")); Group.ConsoleCommandNames.Add(TEXT("ProfileGPU")); ConsoleCommandGroups.Add(Group); Group.ConsoleCommandNames.Empty(); FGDMConsoleCommandPair Pair; Pair.CategoryIndex = 3; /* Profiler */ Pair.CommandNetType = EGDMConsoleCommandNetType::LocalOnly; Pair.Title = FText::FromString(TEXT("Toggle RHISetGPUCaptureOptions")); Pair.Description = FText::FromString(TEXT("BasePass内部の各DrawCall毎の処理負荷を出力する\n(マテリアル名などが見られるようになったり) ※RHIThreadがOffになり、DrawThread負荷が増加する点に注意")); Pair.ClickedEvent = EGDMConsoleCommandClickedEvent::Non; Pair.FirstConsoleCommandName = TEXT("r.RHISetGPUCaptureOptions 1"); Pair.SecondConsoleCommandName = TEXT("r.RHISetGPUCaptureOptions 0"); ConsoleCommandPairs.Add(Pair); Pair.Title = FText::FromString(TEXT("Toggle Profile")); Pair.Description = FText::FromString(TEXT("startfile / stopfile をトグルで実行。プロファイル結果をファイルにue4stats形式で出力する")); Pair.ClickedEvent = EGDMConsoleCommandClickedEvent::MenuClose; Pair.FirstConsoleCommandName = TEXT("stat startfile"); Pair.SecondConsoleCommandName = TEXT("stat stopfile"); ConsoleCommandPairs.Add(Pair); Pair.Title = FText::FromString(TEXT("Toggle CSV Profiler")); Pair.Description = FText::FromString(TEXT("CsvProfile Start / Stop のトグル")); Pair.ClickedEvent = EGDMConsoleCommandClickedEvent::MenuClose; Pair.FirstConsoleCommandName = TEXT("CsvProfile Start"); Pair.SecondConsoleCommandName = TEXT("CsvProfile Stop"); ConsoleCommandPairs.Add(Pair); } void UGameDebugMenuSettings::SetupCategoryDisplay() { FGDMConsoleCommandSingle Single; Single.CategoryIndex = 4; /* Display */ Single.CommandNetType = EGDMConsoleCommandNetType::LocalOnly; Single.Title = FText::FromString(TEXT("Game")); Single.Description = FText::FromString(TEXT("各種 Gameplay のティックの所要時間に関するフィードバックです")); Single.ConsoleCommandName = TEXT("stat Game"); ConsoleCommandNames.Add(Single); Single.Title = FText::FromString(TEXT("Engine")); Single.Description = FText::FromString(TEXT("フレーム時間やレンダリング中のトライアングル数のカウンタなど\n一般的なレンダリング統計情報を表示します")); Single.ConsoleCommandName = TEXT("stat Engine"); ConsoleCommandNames.Add(Single); Single.Title = FText::FromString(TEXT("Init Views")); Single.Description = FText::FromString(TEXT("カリングの所要時間やプリミティブ数などの情報を表示する")); Single.ConsoleCommandName = TEXT("stat InitViews"); ConsoleCommandNames.Add(Single); Single.Title = FText::FromString(TEXT("Scene Rendering")); Single.Description = FText::FromString(TEXT("一般的なレンダリング統計を示す")); Single.ConsoleCommandName = TEXT("stat SceneRendering"); ConsoleCommandNames.Add(Single); Single.Title = FText::FromString(TEXT("GPU")); Single.Description = FText::FromString(TEXT("ProfileGPUのリアルタイム版。GPUのフレーム単位の統計を表示する")); Single.ConsoleCommandName = TEXT("stat GPU"); ConsoleCommandNames.Add(Single); Single.Title = FText::FromString(TEXT("RHI")); Single.Description = FText::FromString(TEXT("全体のDrawCall数などを確認できる")); Single.ConsoleCommandName = TEXT("stat RHI"); ConsoleCommandNames.Add(Single); Single.Title = FText::FromString(TEXT("Particles")); Single.Description = FText::FromString(TEXT("パーティクルの統計情報。今どのくらい出ていて、どのくらい処理がかかっているのかを表示する")); Single.ConsoleCommandName = TEXT("stat GPUParticles"); ConsoleCommandNames.Add(Single); Single.Title = FText::FromString(TEXT("Levels")); Single.Description = FText::FromString(TEXT("現在読み込まれているLevelの表示。読み込みにかかった時間も表示される")); Single.ConsoleCommandName = TEXT("stat Levels"); ConsoleCommandNames.Add(Single); Single.Title = FText::FromString(TEXT("LightRendering")); Single.Description = FText::FromString(TEXT("ライティングとシャドウのレンダリングにかかる時間に関する情報を表示")); Single.ConsoleCommandName = TEXT("stat LightRendering"); ConsoleCommandNames.Add(Single); Single.Title = FText::FromString(TEXT("ShadowRendering")); Single.Description = FText::FromString(TEXT("stat LightRendering で示される実際のシャドウ レンダリング時間とは別に\nシャドウの計算にかかっている時間を表示")); Single.ConsoleCommandName = TEXT("stat ShadowRendering"); ConsoleCommandNames.Add(Single); Single.Title = FText::FromString(TEXT("Streaming")); Single.Description = FText::FromString(TEXT("テクスチャのストリーミング処理で使用しているメモリ量、シーン内に存在するストリーミング中のテクスチャ数など\nストリーミング中のアセットの各種統計情報を表示")); Single.ConsoleCommandName = TEXT("stat Streaming"); ConsoleCommandNames.Add(Single); Single.Title = FText::FromString(TEXT("Streaming Details")); Single.Description = FText::FromString(TEXT("一般的なテクスチャ ストリーミングをさらに特定のグループ\n (ライトマップ、静的 テクスチャ、動的テクスチャ) に分類するなど\nストリーミングに関するより詳しい統計情報を表示")); Single.ConsoleCommandName = TEXT("stat StreamingDetails"); ConsoleCommandNames.Add(Single); Single.Title = FText::FromString(TEXT("Memory")); Single.Description = FText::FromString(TEXT("メモリ使用量の統計を表示する")); Single.ConsoleCommandName = TEXT("stat Memory"); ConsoleCommandNames.Add(Single); Single.Title = FText::FromString(TEXT("Animation")); Single.Description = FText::FromString(TEXT("SkeletalMeshなどのティックごとの計算時間を表示する")); Single.ConsoleCommandName = TEXT("stat Anim"); ConsoleCommandNames.Add(Single); Single.Title = FText::FromString(TEXT("Slate")); Single.Description = FText::FromString(TEXT("Slateで使用するTickなどを表示する")); Single.ConsoleCommandName = TEXT("stat Slate"); ConsoleCommandNames.Add(Single); Single.Title = FText::FromString(TEXT("Hitches")); Single.Description = FText::FromString(TEXT("ヒッチを検知してログに出力する")); Single.ConsoleCommandName = TEXT("stat hitches"); ConsoleCommandNames.Add(Single); Single.Title = FText::FromString(TEXT("Character")); Single.Description = FText::FromString(TEXT("")); Single.ConsoleCommandName = TEXT("stat character"); ConsoleCommandNames.Add(Single); Single.Title = FText::FromString(TEXT("GameplayTags")); Single.Description = FText::FromString(TEXT("")); Single.ConsoleCommandName = TEXT("stat GameplayTags"); ConsoleCommandNames.Add(Single); Single.Title = FText::FromString(TEXT("Collision")); Single.Description = FText::FromString(TEXT("")); Single.ConsoleCommandName = TEXT("stat Collision"); ConsoleCommandNames.Add(Single); Single.Title = FText::FromString(TEXT("Component")); Single.Description = FText::FromString(TEXT("コンポーネントのパフォーマンス情報(Transform更新など)リストを表示する")); Single.ConsoleCommandName = TEXT("stat component"); ConsoleCommandNames.Add(Single); Single.Title = FText::FromString(TEXT("LLM")); Single.Description = FText::FromString(TEXT("Low Level Memory Tracker(LLM)はメモリの使用状況を追跡するツールを使用し\nTag付けされた使用中のメモリを表示\n(LLM有効にはならないので事前に設定すること)")); Single.ConsoleCommandName = TEXT("stat llm"); ConsoleCommandNames.Add(Single); Single.Title = FText::FromString(TEXT("LLMPlatform")); Single.Description = FText::FromString(TEXT("OSの情報を含むメモリ情報を表示")); Single.ConsoleCommandName = TEXT("stat LLMPlatform"); ConsoleCommandNames.Add(Single); Single.Title = FText::FromString(TEXT("UObjects")); Single.Description = FText::FromString(TEXT("ゲーム内の UObjects のパフォーマンスに関する統計情報を表示")); Single.ConsoleCommandName = TEXT("stat UObjects"); ConsoleCommandNames.Add(Single); FGDMConsoleCommandGroup Group; Group.CategoryIndex = 4; /* Display */ Group.CommandNetType = EGDMConsoleCommandNetType::LocalOnly; Group.Title = FText::FromString(TEXT("AI Info")); Group.Description = FText::FromString(TEXT("AIに必要な情報(BehaviorTree,EQS)をまとめて表示する")); Group.ConsoleCommandNames.Add(TEXT("stat AI")); Group.ConsoleCommandNames.Add(TEXT("stat AIBehaviorTree")); Group.ConsoleCommandNames.Add(TEXT("stat AI_EQS")); Group.ConsoleCommandNames.Add(TEXT("stat Navigation")); ConsoleCommandGroups.Add(Group); Group.ConsoleCommandNames.Empty(); Group.Title = FText::FromString(TEXT("All Memory Info")); Group.Description = FText::FromString(TEXT("使用量、他アロケーター情報などを一括表示する")); Group.ConsoleCommandNames.Add(TEXT("stat MemoryPlatform")); Group.ConsoleCommandNames.Add(TEXT("stat MemoryStaticMesh")); Group.ConsoleCommandNames.Add(TEXT("stat MemoryAllocator")); ConsoleCommandGroups.Add(Group); Group.ConsoleCommandNames.Empty(); } void UGameDebugMenuSettings::SetupCategoryShowDebug() { FGDMConsoleCommandPair Pair; Pair.CategoryIndex = 5; /* Show Debug */ Pair.CommandNetType = EGDMConsoleCommandNetType::LocalOnly; Pair.Title = FText::FromString(TEXT("Toggle VisualizeMovement")); Pair.Description = FText::FromString(TEXT("キャラクターの加速度や移動モードを表示させる")); Pair.ClickedEvent = EGDMConsoleCommandClickedEvent::Non; Pair.FirstConsoleCommandName = TEXT("p.VisualizeMovement 1"); Pair.SecondConsoleCommandName = TEXT("p.VisualizeMovement 0"); ConsoleCommandPairs.Add(Pair); Pair.Title = FText::FromString(TEXT("Toggle VisualizeLODs")); Pair.Description = FText::FromString(TEXT("SkinnedMeshComponentで適応してるLODの確認\nボーン数、頂点数などが確認可能(Shipping / Test以外のビルドで有効)\nLOD0:White LOD1:Green LOD2:Yellow LOD3:Red Other:Purple")); Pair.ClickedEvent = EGDMConsoleCommandClickedEvent::Non; Pair.FirstConsoleCommandName = TEXT("a.VisualizeLODs 1"); Pair.SecondConsoleCommandName = TEXT("a.VisualizeLODs 0"); ConsoleCommandPairs.Add(Pair); Pair.Title = FText::FromString(TEXT("Toggle VisualizeOccludedPrimitives")); Pair.Description = FText::FromString(TEXT("オクルードされたアクタを視覚化する")); Pair.ClickedEvent = EGDMConsoleCommandClickedEvent::Non; Pair.FirstConsoleCommandName = TEXT("r.VisualizeOccludedPrimitives 1"); Pair.SecondConsoleCommandName = TEXT("r.VisualizeOccludedPrimitives 0"); ConsoleCommandPairs.Add(Pair); Pair.Title = FText::FromString(TEXT("Toggle LODColoration")); Pair.Description = FText::FromString(TEXT("PrimitiveComponentのLODレベル毎の色で確認\nLOD0:White LOD1:Red LOD2:Green LOD3:Blue LOD4:Yellow LOD5:Fuchisia LOD6:Cyan LOD7:Purple")); Pair.ClickedEvent = EGDMConsoleCommandClickedEvent::Non; Pair.FirstConsoleCommandName = TEXT("ShowFlag.LODColoration 1"); Pair.SecondConsoleCommandName = TEXT("ShowFlag.LODColoration 0"); ConsoleCommandPairs.Add(Pair); FGDMConsoleCommandSingle Single; Single.CategoryIndex = 5; /* Show Debug */ Single.CommandNetType = EGDMConsoleCommandNetType::LocalOnly; Single.Title = FText::FromString(TEXT("Toggle ShowDebug")); Single.Description = FText::FromString(TEXT("HUDを使用したデバック情報を画面に表示するかを切り替える")); Single.ConsoleCommandName = TEXT("ShowDebug"); ConsoleCommandNames.Add(Single); Single.Title = FText::FromString(TEXT("ShowDebug NextDebugTarget")); Single.Description = FText::FromString(TEXT("デバック情報の表示対象アクターを切り替える(次へ進む)")); Single.ConsoleCommandName = TEXT("NextDebugTarget"); ConsoleCommandNames.Add(Single); Single.Title = FText::FromString(TEXT("ShowDebug PreviousDebugTarget")); Single.Description = FText::FromString(TEXT("デバック情報の表示対象アクターを切り替える(戻る)")); Single.ConsoleCommandName = TEXT("PreviousDebugTarget"); ConsoleCommandNames.Add(Single); Single.Title = FText::FromString(TEXT("ShowDebug Reset")); Single.Description = FText::FromString(TEXT("HUDを使用したデバック情報の表示項目をリセットする")); Single.ConsoleCommandName = TEXT("ShowDebug Reset"); ConsoleCommandNames.Add(Single); Single.Title = FText::FromString(TEXT("ShowDebug Input")); Single.Description = FText::FromString(TEXT("入力情報を表示する")); Single.ConsoleCommandName = TEXT("ShowDebug Input"); ConsoleCommandNames.Add(Single); Single.Title = FText::FromString(TEXT("ShowDebug Enhancedinput")); Single.Description = FText::FromString(TEXT("Enhancedinputの入力情報を表示する")); Single.ConsoleCommandName = TEXT("Showdebug Enhancedinput"); ConsoleCommandNames.Add(Single); Single.Title = FText::FromString(TEXT("ShowDebug Collision")); Single.Description = FText::FromString(TEXT("対象アクターのコリジョン情報を表示する")); Single.ConsoleCommandName = TEXT("ShowDebug COLLISION"); ConsoleCommandNames.Add(Single); Single.Title = FText::FromString(TEXT("ShowDebug Physics")); Single.Description = FText::FromString(TEXT("対象アクターのPhysics関係の情報を表示する")); Single.ConsoleCommandName = TEXT("ShowDebug PHYSICS"); ConsoleCommandNames.Add(Single); Single.Title = FText::FromString(TEXT("ShowDebug Forcefeedback")); Single.Description = FText::FromString(TEXT("")); Single.ConsoleCommandName = TEXT("ShowDebug FORCEFEEDBACK"); ConsoleCommandNames.Add(Single); Single.Title = FText::FromString(TEXT("ShowDebug Camera")); Single.Description = FText::FromString(TEXT("アクティブなカメラの情報を表示する")); Single.ConsoleCommandName = TEXT("ShowDebug CAMERA"); ConsoleCommandNames.Add(Single); Single.Title = FText::FromString(TEXT("ShowDebug Animation")); Single.Description = FText::FromString(TEXT("対象アクターのSkeletalMeshのアニメーション情報を表示する")); Single.ConsoleCommandName = TEXT("ShowDebug ANIMATION"); ConsoleCommandNames.Add(Single); Single.Title = FText::FromString(TEXT("ShowDebug Animation Toggle Graph")); Single.Description = FText::FromString(TEXT("アニムグラフの内容を表示を切り替える")); Single.ConsoleCommandName = TEXT("ShowDebugToggleSubCategory GRAPH"); ConsoleCommandNames.Add(Single); Single.Title = FText::FromString(TEXT("ShowDebug Animation Toggle FullGraph")); Single.Description = FText::FromString(TEXT("アニムグラフの内容をすべて表示するかを切り替える")); Single.ConsoleCommandName = TEXT("ShowDebugToggleSubCategory FULLGRAPH"); ConsoleCommandNames.Add(Single); Single.Title = FText::FromString(TEXT("ShowDebug Animation Toggle Curves")); Single.Description = FText::FromString(TEXT("Curve情報を表示するか切り替える")); Single.ConsoleCommandName = TEXT("ShowDebugToggleSubCategory CURVES"); ConsoleCommandNames.Add(Single); Single.Title = FText::FromString(TEXT("ShowDebug Animation Toggle Montages")); Single.Description = FText::FromString(TEXT("モンタージュ情報を表示するか切り替える")); Single.ConsoleCommandName = TEXT("ShowDebugToggleSubCategory MONTAGES"); ConsoleCommandNames.Add(Single); Single.Title = FText::FromString(TEXT("ShowDebug Animation Toggle Notifies")); Single.Description = FText::FromString(TEXT("アニメーション通知情報を表示するか切り替える")); Single.ConsoleCommandName = TEXT("ShowDebugToggleSubCategory NOTIFIES"); ConsoleCommandNames.Add(Single); Single.Title = FText::FromString(TEXT("ShowDebug Animation Toggle SyncGroups")); Single.Description = FText::FromString(TEXT("SyncGroup情報を表示するか切り替える")); Single.ConsoleCommandName = TEXT("ShowDebugToggleSubCategory SYNCGROUPS"); ConsoleCommandNames.Add(Single); Single.Title = FText::FromString(TEXT("ShowDebug Animation Toggle FullBlendSpaceDisplay")); Single.Description = FText::FromString(TEXT("ブレンドスペースの重み値の表示を切り替える(SYNCGROUPSが表示されるとき反映)")); Single.ConsoleCommandName = TEXT("ShowDebugToggleSubCategory FULLBLENDSPACEDISPLAY"); ConsoleCommandNames.Add(Single); } void UGameDebugMenuSettings::SetupCategoryViewMode() { FGDMConsoleCommandSingle Single; Single.CategoryIndex = 6; /* ViewMode */ Single.CommandNetType = EGDMConsoleCommandNetType::LocalOnly; Single.Title = FText::FromString(TEXT("Lit")); Single.Description = FText::FromString(TEXT("シーン全てのライティングがある状態\nエディターでかつStandaloneのみ有効\n(Package環境で使用したい場合は「r.ForceDebugViewModes」を1に)")); Single.ConsoleCommandName = TEXT("viewmode lit"); ConsoleCommandNames.Add(Single); Single.Title = FText::FromString(TEXT("Unlit")); Single.Description = FText::FromString(TEXT("シーン全てのライティングを切った状態\nエディターでかつStandaloneのみ有効\n(Package環境で使用したい場合は「r.ForceDebugViewModes」を1に)")); Single.ConsoleCommandName = TEXT("viewmode unlit"); ConsoleCommandNames.Add(Single); Single.Title = FText::FromString(TEXT("Wireframe")); Single.Description = FText::FromString(TEXT("シーン内のすべてのポリゴンエッジを表示")); Single.ConsoleCommandName = TEXT("viewmode Wireframe"); ConsoleCommandNames.Add(Single); Single.Title = FText::FromString(TEXT("Detaillighting")); Single.Description = FText::FromString(TEXT("シーン全体に中間色のマテリアルをアクティベートする\n基本色が暗すぎたり、ライティングが不明瞭になっている場合の分離に有効\nエディターでかつStandaloneのみ有効\n(Package環境で使用したい場合は「r.ForceDebugViewModes」を1に)")); Single.ConsoleCommandName = TEXT("viewmode lit_detaillighting"); ConsoleCommandNames.Add(Single); Single.Title = FText::FromString(TEXT("Lighting Only")); Single.Description = FText::FromString(TEXT("ライティングにのみ影響を受ける中間色のマテリアルを表示\nエディターでかつStandaloneのみ有効\n(Package環境で使用したい場合は「r.ForceDebugViewModes」を1に)")); Single.ConsoleCommandName = TEXT("viewmode lightingonly"); ConsoleCommandNames.Add(Single); Single.Title = FText::FromString(TEXT("Reflection")); Single.Description = FText::FromString(TEXT("反射の表示\nエディターでかつStandaloneのみ有効\n(Package環境で使用したい場合は「r.ForceDebugViewModes」を1に)")); Single.ConsoleCommandName = TEXT("viewmode ReflectionOverride"); ConsoleCommandNames.Add(Single); Single.Title = FText::FromString(TEXT("Light Complexity")); Single.Description = FText::FromString(TEXT("ジオメトリに影響を与える非静的ライトの数を表示\nライトがサーフェスに影響を与えるほど、描画負荷が高くなる\nエディターでかつStandaloneのみ有効\n(Package環境で使用したい場合は「r.ForceDebugViewModes」を1に)")); Single.ConsoleCommandName = TEXT("viewmode lightcomplexity"); ConsoleCommandNames.Add(Single); Single.Title = FText::FromString(TEXT("Lightmap Density")); Single.Description = FText::FromString(TEXT("テクスチャ マッピングされるオブジェクトのライトマップ密度を表示\n理想的/最大限の密度設定別に色分けして、実際のライトマップ テクセルへマッピングするグリッドを表示\nエディターでかつStandaloneのみ有効\n(Package環境で使用したい場合は「r.ForceDebugViewModes」を1に)")); Single.ConsoleCommandName = TEXT("viewmode LightMapDensity"); ConsoleCommandNames.Add(Single); Single.Title = FText::FromString(TEXT("Shader Complexity")); Single.Description = FText::FromString(TEXT("シーンの各ピクセルの計算に使用しているシェーダ命令数を視覚化する\nエディターでかつStandaloneのみ有効\n(Package環境で使用したい場合は「r.ForceDebugViewModes」を1に)")); Single.ConsoleCommandName = TEXT("viewmode shadercomplexity"); ConsoleCommandNames.Add(Single); Single.Title = FText::FromString(TEXT("ShaderComplexity With Quadoverdraw")); Single.Description = FText::FromString(TEXT("エディターでかつStandaloneのみ有効\n(Package環境で使用したい場合は「r.ForceDebugViewModes」を1に)")); Single.ConsoleCommandName = TEXT("viewmode ShaderComplexityWithQuadOverdraw"); ConsoleCommandNames.Add(Single); Single.Title = FText::FromString(TEXT("Quadoverdraw")); Single.Description = FText::FromString(TEXT("エディターでかつStandaloneのみ有効\n(Package環境で使用したい場合は「r.ForceDebugViewModes」を1に)")); Single.ConsoleCommandName = TEXT("viewmode Quadoverdraw"); ConsoleCommandNames.Add(Single); /* EditorOnly */ Single.Title = FText::FromString(TEXT("Stationary Light Overlap")); Single.Description = FText::FromString(TEXT("Stationary Lightの重なりを視覚化する\nエディターでのみ有効")); Single.ConsoleCommandName = TEXT("show StationaryLightOverlap"); EditorOnlyConsoleCommandNames.Add(Single); Single.Title = FText::FromString(TEXT("Visualize Buffer")); Single.Description = FText::FromString(TEXT("各レンダーバッファの可視化\nエディターでのみ有効")); Single.ConsoleCommandName = TEXT("show VisualizeBuffer"); EditorOnlyConsoleCommandNames.Add(Single); /* EditorOnly */ Single.Title = FText::FromString(TEXT("Collision")); Single.Description = FText::FromString(TEXT("コリジョン(非)表示")); Single.ConsoleCommandName = TEXT("show collision"); ConsoleCommandNames.Add(Single); Single.Title = FText::FromString(TEXT("Collision Pawn")); Single.Description = FText::FromString(TEXT("")); Single.ConsoleCommandName = TEXT("show CollisionPawn"); ConsoleCommandNames.Add(Single); Single.Title = FText::FromString(TEXT("Collision Visibility")); Single.Description = FText::FromString(TEXT("")); Single.ConsoleCommandName = TEXT("show CollisionVisibility"); ConsoleCommandNames.Add(Single); Single.Title = FText::FromString(TEXT("Bounds")); Single.Description = FText::FromString(TEXT("各Actorのバウンディングボックスの表示")); Single.ConsoleCommandName = TEXT("show bounds"); ConsoleCommandNames.Add(Single); Single.Title = FText::FromString(TEXT("Shadow Frustums")); Single.Description = FText::FromString(TEXT("動的な影を生成しているフラスタムを表示")); Single.ConsoleCommandName = TEXT("show ShadowFrustums"); ConsoleCommandNames.Add(Single); Single.Title = FText::FromString(TEXT("Dynamic Shadows")); Single.Description = FText::FromString(TEXT("全ての動的シャドウを切り替え (シャドウマップ レンダリングとシャドウ フィルタリング / プロジェクション)")); Single.ConsoleCommandName = TEXT("show DynamicShadows"); ConsoleCommandNames.Add(Single); Single.Title = FText::FromString(TEXT("Volumes")); Single.Description = FText::FromString(TEXT("AVolume継承アクターのボリュームを(非)表示")); Single.ConsoleCommandName = TEXT("show Volumes"); ConsoleCommandNames.Add(Single); Single.Title = FText::FromString(TEXT("Navigation")); Single.Description = FText::FromString(TEXT("ナビメッシュの(非)表示")); Single.ConsoleCommandName = TEXT("show Navigation"); ConsoleCommandNames.Add(Single); Single.Title = FText::FromString(TEXT("Deferred Lighting")); Single.Description = FText::FromString(TEXT("すべてのディファード ライティング パスを切り替え")); Single.ConsoleCommandName = TEXT("show DeferredLighting"); ConsoleCommandNames.Add(Single); Single.Title = FText::FromString(TEXT("DirectionalLights")); Single.Description = FText::FromString(TEXT("ディレクショナルライトの表示切り替え")); Single.ConsoleCommandName = TEXT("show DirectionalLights"); ConsoleCommandNames.Add(Single); Single.Title = FText::FromString(TEXT("PointLights")); Single.Description = FText::FromString(TEXT("ポイントライトの表示切り替え")); Single.ConsoleCommandName = TEXT("show PointLights"); ConsoleCommandNames.Add(Single); Single.Title = FText::FromString(TEXT("SpotLights")); Single.Description = FText::FromString(TEXT("スポットライトの表示切り替え")); Single.ConsoleCommandName = TEXT("show SpotLights"); ConsoleCommandNames.Add(Single); Single.Title = FText::FromString(TEXT("Rendering")); Single.Description = FText::FromString(TEXT("")); Single.ConsoleCommandName = TEXT("show Rendering"); ConsoleCommandNames.Add(Single); Single.Title = FText::FromString(TEXT("SkyLighting")); Single.Description = FText::FromString(TEXT("")); Single.ConsoleCommandName = TEXT("show SkyLighting"); ConsoleCommandNames.Add(Single); Single.Title = FText::FromString(TEXT("Decals")); Single.Description = FText::FromString(TEXT("デカールの表示切り替え")); Single.ConsoleCommandName = TEXT("show Decals"); ConsoleCommandNames.Add(Single); Single.Title = FText::FromString(TEXT("Post Processing")); Single.Description = FText::FromString(TEXT("")); Single.ConsoleCommandName = TEXT("show PostProcessing"); ConsoleCommandNames.Add(Single); Single.Title = FText::FromString(TEXT("Particles")); Single.Description = FText::FromString(TEXT("")); Single.ConsoleCommandName = TEXT("show Particles"); ConsoleCommandNames.Add(Single); Single.Title = FText::FromString(TEXT("Specular")); Single.Description = FText::FromString(TEXT("")); Single.ConsoleCommandName = TEXT("show Specular"); ConsoleCommandNames.Add(Single); Single.Title = FText::FromString(TEXT("Translucency")); Single.Description = FText::FromString(TEXT("")); Single.ConsoleCommandName = TEXT("show Translucency"); ConsoleCommandNames.Add(Single); Single.Title = FText::FromString(TEXT("Diffuse")); Single.Description = FText::FromString(TEXT("")); Single.ConsoleCommandName = TEXT("show Diffuse"); ConsoleCommandNames.Add(Single); Single.Title = FText::FromString(TEXT("ReflectionEnvironment")); Single.Description = FText::FromString(TEXT("")); Single.ConsoleCommandName = TEXT("show ReflectionEnvironment"); ConsoleCommandNames.Add(Single); Single.Title = FText::FromString(TEXT("Refraction")); Single.Description = FText::FromString(TEXT("")); Single.ConsoleCommandName = TEXT("show Refraction"); ConsoleCommandNames.Add(Single); Single.Title = FText::FromString(TEXT("AmbientOcclusion")); Single.Description = FText::FromString(TEXT("")); Single.ConsoleCommandName = TEXT("show AmbientOcclusion"); ConsoleCommandNames.Add(Single); Single.Title = FText::FromString(TEXT("GlobalIllumination")); Single.Description = FText::FromString(TEXT("ベイクされた、動的な間接ライティングを切り替え")); Single.ConsoleCommandName = TEXT("show GlobalIllumination"); ConsoleCommandNames.Add(Single); Single.Title = FText::FromString(TEXT("LightFunctions")); Single.Description = FText::FromString(TEXT("ライト関数のレンダリングを切り替え")); Single.ConsoleCommandName = TEXT("show LightFunctions"); ConsoleCommandNames.Add(Single); Single.Title = FText::FromString(TEXT("DepthOfField")); Single.Description = FText::FromString(TEXT("")); Single.ConsoleCommandName = TEXT("show DepthOfField"); ConsoleCommandNames.Add(Single); Single.Title = FText::FromString(TEXT("AntiAliasing")); Single.Description = FText::FromString(TEXT("")); Single.ConsoleCommandName = TEXT("show AntiAliasing"); ConsoleCommandNames.Add(Single); Single.Title = FText::FromString(TEXT("Bloom")); Single.Description = FText::FromString(TEXT("")); Single.ConsoleCommandName = TEXT("show Bloom"); ConsoleCommandNames.Add(Single); Single.Title = FText::FromString(TEXT("StaticMeshes")); Single.Description = FText::FromString(TEXT("")); Single.ConsoleCommandName = TEXT("show StaticMeshes"); ConsoleCommandNames.Add(Single); Single.Title = FText::FromString(TEXT("SkeletalMeshes")); Single.Description = FText::FromString(TEXT("")); Single.ConsoleCommandName = TEXT("show SkeletalMeshes"); ConsoleCommandNames.Add(Single); Single.Title = FText::FromString(TEXT("Landscape")); Single.Description = FText::FromString(TEXT("")); Single.ConsoleCommandName = TEXT("show Landscape"); ConsoleCommandNames.Add(Single); Single.Title = FText::FromString(TEXT("Tessellation")); Single.Description = FText::FromString(TEXT("テッセレーションを切り替え (テッセレーション シェーダーは実行されたまま)")); Single.ConsoleCommandName = TEXT("show Tessellation"); ConsoleCommandNames.Add(Single); Single.Title = FText::FromString(TEXT("VisualizeVolumetricLightmap")); Single.Description = FText::FromString(TEXT("ボリュームライトマップの可視化")); Single.ConsoleCommandName = TEXT("show VisualizeVolumetricLightmap"); ConsoleCommandNames.Add(Single); Single.Title = FText::FromString(TEXT("ScreenSpaceReflections")); Single.Description = FText::FromString(TEXT("スクリーン スペースの反射を切り替え")); Single.ConsoleCommandName = TEXT("show ScreenSpaceReflections"); ConsoleCommandNames.Add(Single); Single.Title = FText::FromString(TEXT("Shader Complexity")); Single.Description = FText::FromString(TEXT("シーンの各ピクセルを計算するために使用されているシェーダ命令の数を視覚化")); Single.ConsoleCommandName = TEXT("show ShaderComplexity"); ConsoleCommandNames.Add(Single); /* EditorOnly */ Single.Title = FText::FromString(TEXT("Materials")); Single.Description = FText::FromString(TEXT("マテリアルのオンオフ\nエディターでのみ有効")); Single.ConsoleCommandName = TEXT("show Materials"); EditorOnlyConsoleCommandNames.Add(Single); /* EditorOnly */ } void UGameDebugMenuSettings::SetupCategoryScalability() { FGDMConsoleCommandNumber Number; Number.CategoryIndex = 7; /* Scalability */ Number.CommandNetType = EGDMConsoleCommandNetType::LocalOnly; Number.PreConsoleCommandName = TEXT("sg."); Number.PostConsoleCommandName = TEXT(" "); Number.DefaultValue = 0.0f; Number.Title = FText::FromString(TEXT("ViewDistanceQuality")); Number.Description = FText::FromString(TEXT("")); Number.ConsoleCommandName = TEXT("ViewDistanceQuality"); Number.ConsoleVariableName = TEXT("sg.ViewDistanceQuality"); Number.UIConfigInfo.Range.bUseMax = true; Number.UIConfigInfo.Range.bUseMin = true; Number.UIConfigInfo.Range.MinValue = 0.0f; Number.UIConfigInfo.Range.MaxValue = 3.0f; Number.UIConfigInfo.DefaultChangeAmount = 1.0f; Number.UIConfigInfo.MaxChangeAmount = 1.0f; ConsoleCommandNumbers.Add(Number); Number.Title = FText::FromString(TEXT("AntiAliasingQuality")); Number.Description = FText::FromString(TEXT("")); Number.ConsoleCommandName = TEXT("AntiAliasingQuality"); Number.ConsoleVariableName = TEXT("sg.AntiAliasingQuality"); Number.UIConfigInfo.Range.bUseMax = true; Number.UIConfigInfo.Range.bUseMin = true; Number.UIConfigInfo.Range.MinValue = 0.0f; Number.UIConfigInfo.Range.MaxValue = 6.0f; Number.UIConfigInfo.DefaultChangeAmount = 1.0f; Number.UIConfigInfo.MaxChangeAmount = 1.0f; ConsoleCommandNumbers.Add(Number); Number.Title = FText::FromString(TEXT("PostProcessQuality")); Number.Description = FText::FromString(TEXT("")); Number.ConsoleCommandName = TEXT("PostProcessQuality"); Number.ConsoleVariableName = TEXT("sg.PostProcessQuality"); Number.UIConfigInfo.Range.bUseMax = true; Number.UIConfigInfo.Range.bUseMin = true; Number.UIConfigInfo.Range.MinValue = 0.0f; Number.UIConfigInfo.Range.MaxValue = 3.0f; Number.UIConfigInfo.DefaultChangeAmount = 1.0f; Number.UIConfigInfo.MaxChangeAmount = 1.0f; ConsoleCommandNumbers.Add(Number); Number.Title = FText::FromString(TEXT("ShadowQuality")); Number.Description = FText::FromString(TEXT("")); Number.ConsoleCommandName = TEXT("ShadowQuality"); Number.ConsoleVariableName = TEXT("sg.ShadowQuality"); Number.UIConfigInfo.Range.bUseMax = true; Number.UIConfigInfo.Range.bUseMin = true; Number.UIConfigInfo.Range.MinValue = 0.0f; Number.UIConfigInfo.Range.MaxValue = 3.0f; Number.UIConfigInfo.DefaultChangeAmount = 1.0f; Number.UIConfigInfo.MaxChangeAmount = 1.0f; ConsoleCommandNumbers.Add(Number); Number.Title = FText::FromString(TEXT("TextureQuality")); Number.Description = FText::FromString(TEXT("")); Number.ConsoleCommandName = TEXT("TextureQuality"); Number.ConsoleVariableName = TEXT("sg.TextureQuality"); Number.UIConfigInfo.Range.bUseMax = true; Number.UIConfigInfo.Range.bUseMin = true; Number.UIConfigInfo.Range.MinValue = 0.0f; Number.UIConfigInfo.Range.MaxValue = 3.0f; Number.UIConfigInfo.DefaultChangeAmount = 1.0f; Number.UIConfigInfo.MaxChangeAmount = 1.0f; ConsoleCommandNumbers.Add(Number); Number.Title = FText::FromString(TEXT("EffectsQuality")); Number.Description = FText::FromString(TEXT("")); Number.ConsoleCommandName = TEXT("EffectsQuality"); Number.ConsoleVariableName = TEXT("sg.EffectsQuality"); Number.UIConfigInfo.Range.bUseMax = true; Number.UIConfigInfo.Range.bUseMin = true; Number.UIConfigInfo.Range.MinValue = 0.0f; Number.UIConfigInfo.Range.MaxValue = 3.0f; Number.UIConfigInfo.DefaultChangeAmount = 1.0f; Number.UIConfigInfo.MaxChangeAmount = 1.0f; ConsoleCommandNumbers.Add(Number); Number.Title = FText::FromString(TEXT("FoliageQuality")); Number.Description = FText::FromString(TEXT("")); Number.ConsoleCommandName = TEXT("FoliageQuality"); Number.ConsoleVariableName = TEXT("sg.FoliageQuality"); Number.UIConfigInfo.Range.bUseMax = true; Number.UIConfigInfo.Range.bUseMin = true; Number.UIConfigInfo.Range.MinValue = 0.0f; Number.UIConfigInfo.Range.MaxValue = 3.0f; Number.UIConfigInfo.DefaultChangeAmount = 1.0f; Number.UIConfigInfo.MaxChangeAmount = 1.0f; ConsoleCommandNumbers.Add(Number); Number.Title = FText::FromString(TEXT("ShadingQuality")); Number.Description = FText::FromString(TEXT("")); Number.ConsoleCommandName = TEXT("ShadingQuality"); Number.ConsoleVariableName = TEXT("sg.ShadingQuality"); Number.UIConfigInfo.Range.bUseMax = true; Number.UIConfigInfo.Range.bUseMin = true; Number.UIConfigInfo.Range.MinValue = 0.0f; Number.UIConfigInfo.Range.MaxValue = 3.0f; Number.UIConfigInfo.DefaultChangeAmount = 1.0f; Number.UIConfigInfo.MaxChangeAmount = 1.0f; ConsoleCommandNumbers.Add(Number); Number.PreConsoleCommandName = TEXT("r."); Number.PostConsoleCommandName = TEXT(" "); Number.Title = FText::FromString(TEXT("ScreenPercentage")); Number.Description = FText::FromString(TEXT("")); Number.ConsoleCommandName = TEXT("ScreenPercentage"); Number.ConsoleVariableName = TEXT("r.ScreenPercentage"); Number.UIConfigInfo.Range.bUseMax = true; Number.UIConfigInfo.Range.bUseMin = true; Number.UIConfigInfo.Range.MinValue = 10.0f; Number.UIConfigInfo.Range.MaxValue = 200.0f; Number.UIConfigInfo.DefaultChangeAmount = 1.0f; Number.UIConfigInfo.MaxChangeAmount = 5.0f; ConsoleCommandNumbers.Add(Number); Number.Title = FText::FromString(TEXT("DynamicRes.OperationMode")); Number.Description = FText::FromString(TEXT("動的解像度の使用方法の切り替え\n 0 = 無効\n 1 = GameUserSettingsの設定に基づいて有効化\n2 = GameUserSettingsを考慮せず有効化")); Number.ConsoleCommandName = TEXT("DynamicRes.OperationMode"); Number.ConsoleVariableName = TEXT("r.DynamicRes.OperationMode"); Number.UIConfigInfo.Range.bUseMax = true; Number.UIConfigInfo.Range.bUseMin = true; Number.UIConfigInfo.Range.MinValue = 0.0f; Number.UIConfigInfo.Range.MaxValue = 2.0f; Number.UIConfigInfo.DefaultChangeAmount = 1.0f; Number.UIConfigInfo.MaxChangeAmount = 1.0f; ConsoleCommandNumbers.Add(Number); Number.Title = FText::FromString(TEXT("MaterialQualityLevel")); Number.Description = FText::FromString(TEXT("マテリアル品質レベルの変更\n0 = Low: 1 = High: 2 = Medium")); Number.ConsoleCommandName = TEXT("MaterialQualityLevel"); Number.ConsoleVariableName = TEXT("r.MaterialQualityLevel"); Number.UIConfigInfo.Range.bUseMax = true; Number.UIConfigInfo.Range.bUseMin = true; Number.UIConfigInfo.Range.MinValue = 0.0f; Number.UIConfigInfo.Range.MaxValue = 2.0f; Number.UIConfigInfo.DefaultChangeAmount = 1.0f; Number.UIConfigInfo.MaxChangeAmount = 1.0f; ConsoleCommandNumbers.Add(Number); Number.PreConsoleCommandName = TEXT("r.DOF."); Number.Title = FText::FromString(TEXT("DOF - MaxBackgroundRadius")); Number.Description = FText::FromString(TEXT("水平スクリーン空間のバックグラウンド ブラー半径の最大サイズ")); Number.ConsoleCommandName = TEXT("Kernel.MaxBackgroundRadius"); Number.ConsoleVariableName = TEXT("r.DOF.Kernel.MaxBackgroundRadius"); Number.UIConfigInfo.Range.bUseMax = true; Number.UIConfigInfo.Range.bUseMin = true; Number.UIConfigInfo.Range.MinValue = 0.0f; Number.UIConfigInfo.Range.MaxValue = 1.0f; Number.UIConfigInfo.DefaultChangeAmount = 0.001f; Number.UIConfigInfo.MaxChangeAmount = 0.005f; ConsoleCommandNumbers.Add(Number); Number.Title = FText::FromString(TEXT("DOF - MaxForegroundRadius")); Number.Description = FText::FromString(TEXT("水平スクリーン空間のフォアグラウンド ブラー半径の最大サイズ")); Number.ConsoleCommandName = TEXT("Kernel.MaxForegroundRadius"); Number.ConsoleVariableName = TEXT("r.DOF.Kernel.MaxForegroundRadius"); Number.UIConfigInfo.Range.bUseMax = true; Number.UIConfigInfo.Range.bUseMin = true; Number.UIConfigInfo.Range.MinValue = 0.0f; Number.UIConfigInfo.Range.MaxValue = 1.0f; Number.UIConfigInfo.DefaultChangeAmount = 0.001f; Number.UIConfigInfo.MaxChangeAmount = 0.005f; ConsoleCommandNumbers.Add(Number); Number.Title = FText::FromString(TEXT("DOF - MaxSpriteRatio")); Number.Description = FText::FromString(TEXT("スプライトとしての散乱ピクセル クワッドの最大比率\nDOF の散乱の上限をコントロールするために便利\n1では100%のピクセルクワッドを散乱可能に")); Number.ConsoleCommandName = TEXT("Scatter.MaxSpriteRatio"); Number.ConsoleVariableName = TEXT("r.DOF.Scatter.MaxSpriteRatio"); Number.UIConfigInfo.Range.bUseMax = true; Number.UIConfigInfo.Range.bUseMin = true; Number.UIConfigInfo.Range.MinValue = 0.0f; Number.UIConfigInfo.Range.MaxValue = 1.0f; Number.UIConfigInfo.DefaultChangeAmount = 0.01f; Number.UIConfigInfo.MaxChangeAmount = 0.05f; ConsoleCommandNumbers.Add(Number); FGDMConsoleCommandPair Pair; Pair.CategoryIndex = 7; /* Scalability */ Pair.CommandNetType = EGDMConsoleCommandNetType::LocalOnly; Pair.Title = FText::FromString(TEXT("Toggle VSync")); Pair.Description = FText::FromString(TEXT("垂直同期の切り替え")); Pair.ClickedEvent = EGDMConsoleCommandClickedEvent::Non; Pair.FirstConsoleCommandName = TEXT("r.VSync 0"); Pair.SecondConsoleCommandName = TEXT("r.VSync 1"); ConsoleCommandPairs.Add(Pair); } void UGameDebugMenuSettings::SetupCategoryFreeze() { FGDMConsoleCommandSingle Single; Single.CategoryIndex = 8; /* Freeze */ Single.CommandNetType = EGDMConsoleCommandNetType::LocalOnly; Single.ClickedEvent = EGDMConsoleCommandClickedEvent::Non; Single.Title = FText::FromString(TEXT("Freeze Rendering")); Single.Description = FText::FromString(TEXT("レベル内にあるオクルードされたアクタ\nおよび表示されているアクタの現在のレンダリングの状態を一時停止 / 解除する")); Single.ConsoleCommandName = TEXT("FreezeRendering"); ConsoleCommandNames.Add(Single); Single.Title = FText::FromString(TEXT("FreezeParticleSimulation")); Single.Description = FText::FromString(TEXT("レベル内のすべての CPU スプライト パーティクル シミュレーション\nの一時停止 / 解除する")); Single.ConsoleCommandName = TEXT("FX.FreezeParticleSimulation"); ConsoleCommandNames.Add(Single); Single.Title = FText::FromString(TEXT("FreezeGPUSimulation")); Single.Description = FText::FromString(TEXT("レベル内のすべての GPU スプライト パーティクル シミュレーション\nの一時停止 / 解除する")); Single.ConsoleCommandName = TEXT("FX.FreezeGPUSimulation"); ConsoleCommandNames.Add(Single); FGDMConsoleCommandPair Pair; Pair.CategoryIndex = 8; /* Freeze */ Pair.CommandNetType = EGDMConsoleCommandNetType::LocalOnly; Pair.Title = FText::FromString(TEXT("Foliage Freeze / Unfreeze")); Pair.Description = FText::FromString(TEXT("レベル内でオクルードおよび表示された\nペイントされたフォリッジ クラスタの現在のレンダリングの状態を一時停止 / 解除する")); Pair.ClickedEvent = EGDMConsoleCommandClickedEvent::Non; Pair.FirstConsoleCommandName = TEXT("Foliage.Freeze"); Pair.SecondConsoleCommandName = TEXT("Foliage.Unfreeze"); ConsoleCommandPairs.Add(Pair); } void UGameDebugMenuSettings::SetupCategoryDumpLogs() { FGDMConsoleCommandSingle Single; Single.CategoryIndex = 9; /* Dump Logs */ Single.CommandNetType = EGDMConsoleCommandNetType::LocalOnly; Single.Title = FText::FromString(TEXT("Obj List")); Single.Description = FText::FromString(TEXT("OutputLogウィンドウに各オブジェクトの数と使用メモリ量を出力する")); Single.ConsoleCommandName = TEXT("Obj List"); ConsoleCommandNames.Add(Single); Single.Title = FText::FromString(TEXT("Obj List Forget")); Single.Description = FText::FromString(TEXT("Obj Listで取得したObjectを記憶し\n これ以降のObj Listでは差分が表示されるようになる")); Single.ConsoleCommandName = TEXT("Obj List Forget"); ConsoleCommandNames.Add(Single); Single.Title = FText::FromString(TEXT("Obj List Remember")); Single.Description = FText::FromString(TEXT("Obj List Forget で記憶したオブジェクトをリセットする")); Single.ConsoleCommandName = TEXT("Obj List Remember"); ConsoleCommandNames.Add(Single); Single.Title = FText::FromString(TEXT("MemReport")); Single.Description = FText::FromString(TEXT("YourGame/Saved/Profiling/MemReports以下にメモリ使用ログを出力する")); Single.ConsoleCommandName = TEXT("memreport"); ConsoleCommandNames.Add(Single); Single.Title = FText::FromString(TEXT("MemReport Full")); Single.Description = FText::FromString(TEXT("MemReportのより詳細にログを出力します")); Single.ConsoleCommandName = TEXT("memreport -full"); ConsoleCommandNames.Add(Single); Single.Title = FText::FromString(TEXT("Dump Ticks")); Single.Description = FText::FromString(TEXT("Level上に存在するActorやComponentのTickが有効なものをリストアップしログ表示する")); Single.ConsoleCommandName = TEXT("dumpticks Enable"); ConsoleCommandNames.Add(Single); Single.Title = FText::FromString(TEXT("Loadtimes Dumpreport")); Single.Description = FText::FromString(TEXT("ロードされたファイルのロード時間を降順でログ出力する\n(loadtimes.resetでタイミング調整可能)")); Single.ConsoleCommandName = TEXT("loadtimes.dumpreport"); ConsoleCommandNames.Add(Single); Single.Title = FText::FromString(TEXT("Dump Hitches")); Single.Description = FText::FromString(TEXT("t.HitchFrameTimeThreshold に基づいて処理落ちが検出されるたびに\nログへ書き込まれる")); Single.ConsoleCommandName = TEXT("stat DumpHitches"); ConsoleCommandNames.Add(Single); Single.Title = FText::FromString(TEXT("Log Garbage")); Single.Description = FText::FromString(TEXT("GCが起きたときの検索、削除コストをログに表示する")); Single.ConsoleCommandName = TEXT("log LogGarbage log"); ConsoleCommandNames.Add(Single); Single.Title = FText::FromString(TEXT("Log List")); Single.Description = FText::FromString(TEXT("各ログカテゴリの一覧と、各カテゴリの現在の設定を確認できる")); Single.ConsoleCommandName = TEXT("log list"); ConsoleCommandNames.Add(Single); FGDMConsoleCommandNumber Number; Number.CategoryIndex = 9; /* Dump Logs */ Number.CommandNetType = EGDMConsoleCommandNetType::LocalOnly; Number.Title = FText::FromString(TEXT("Hitch Frame Time Threshold")); Number.Description = FText::FromString(TEXT("DumpHitches実行時ログに出力するかどうかのしきい値(ミリ秒)\n この値よりも処理に時間がかかるものがログに出力されるようになる")); Number.PreConsoleCommandName = TEXT("t."); Number.ConsoleCommandName = TEXT("HitchFrameTimeThreshold"); Number.PostConsoleCommandName = TEXT(" "); Number.ConsoleVariableName = TEXT("t.HitchFrameTimeThreshold"); Number.UIConfigInfo.Range.bUseMin = true; Number.UIConfigInfo.Range.bUseMax = false; Number.UIConfigInfo.Range.MinValue = 10.0f; Number.UIConfigInfo.DefaultChangeAmount = 5.0f; Number.UIConfigInfo.MaxChangeAmount = 10.0f; Number.DefaultValue = FEnginePerformanceTargets::GetHitchFrameTimeThresholdMS(); ConsoleCommandNumbers.Add(Number); } void UGameDebugMenuSettings::SetupCategoryNetwork() { FGDMConsoleCommandNumber Number; Number.CategoryIndex = 10; /* Network */ Number.ConsoleVariableName = TEXT(""); /* NetEmulation系は FConsoleCommandWithWorldAndArgsDelegate で定義されてるため * IConsoleObjectから値が取れない。。。 */ Number.Title = FText::FromString(TEXT("PktLag")); Number.Description = FText::FromString(TEXT("パケット送信を遅らせる(ミリ秒)")); Number.PreConsoleCommandName = TEXT("NetEmulation."); Number.ConsoleCommandName = TEXT("PktLag"); Number.PostConsoleCommandName = TEXT(" "); Number.DefaultValue = 0.0f; Number.CommandNetType = EGDMConsoleCommandNetType::ServerAll; Number.UIConfigInfo.Range.bUseMax = false; Number.UIConfigInfo.Range.bUseMin = true; Number.UIConfigInfo.Range.MinValue = 0.0f; Number.UIConfigInfo.Range.MaxValue = 1.0f; Number.UIConfigInfo.DefaultChangeAmount = 10; Number.UIConfigInfo.MaxChangeAmount = 100; ConsoleCommandNumbers.Add(Number); Number.Title = FText::FromString(TEXT("PktLagVariance")); Number.Description = FText::FromString(TEXT("パケット送信を指定範囲内でランダムに遅らせる(±ミリ秒)")); Number.PreConsoleCommandName = TEXT("NetEmulation."); Number.ConsoleCommandName = TEXT("PktLagVariance"); Number.PostConsoleCommandName = TEXT(" "); Number.DefaultValue = 0.0f; Number.CommandNetType = EGDMConsoleCommandNetType::ServerAll; Number.UIConfigInfo.Range.bUseMax = false; Number.UIConfigInfo.Range.bUseMin = true; Number.UIConfigInfo.Range.MinValue = 0.0f; Number.UIConfigInfo.Range.MaxValue = 1.0f; Number.UIConfigInfo.DefaultChangeAmount = 10; Number.UIConfigInfo.MaxChangeAmount = 100; ConsoleCommandNumbers.Add(Number); Number.Title = FText::FromString(TEXT("PktLoss")); Number.Description = FText::FromString(TEXT("一定の確率でパケットを送信しないようにする(0~100%)")); Number.PreConsoleCommandName = TEXT("NetEmulation."); Number.ConsoleCommandName = TEXT("PktLoss"); Number.PostConsoleCommandName = TEXT(" "); Number.DefaultValue = 0.0f; Number.CommandNetType = EGDMConsoleCommandNetType::ServerAll; Number.UIConfigInfo.Range.bUseMax = true; Number.UIConfigInfo.Range.bUseMin = true; Number.UIConfigInfo.Range.MinValue = 0.0f; Number.UIConfigInfo.Range.MaxValue = 100.0f; Number.UIConfigInfo.DefaultChangeAmount = 1; Number.UIConfigInfo.MaxChangeAmount = 10; ConsoleCommandNumbers.Add(Number); Number.Title = FText::FromString(TEXT("PktOrder")); Number.Description = FText::FromString(TEXT("パケットをバッファリングして送信順序をランダムにする(1=有効、0=無効)")); Number.PreConsoleCommandName = TEXT("NetEmulation."); Number.ConsoleCommandName = TEXT("PktOrder"); Number.PostConsoleCommandName = TEXT(" "); Number.DefaultValue = 0.0f; Number.CommandNetType = EGDMConsoleCommandNetType::ServerAll; Number.UIConfigInfo.Range.bUseMax = true; Number.UIConfigInfo.Range.bUseMin = true; Number.UIConfigInfo.Range.MinValue = 0.0f; Number.UIConfigInfo.Range.MaxValue = 1.0f; Number.UIConfigInfo.DefaultChangeAmount = 1; Number.UIConfigInfo.MaxChangeAmount = 1; ConsoleCommandNumbers.Add(Number); Number.Title = FText::FromString(TEXT("PktDup")); Number.Description = FText::FromString(TEXT("パケット送信時に指定確率で重複送信する(0~100%)")); Number.PreConsoleCommandName = TEXT("NetEmulation."); Number.ConsoleCommandName = TEXT("PktDup"); Number.PostConsoleCommandName = TEXT(" "); Number.DefaultValue = 0.0f; Number.CommandNetType = EGDMConsoleCommandNetType::ServerAll; Number.UIConfigInfo.Range.bUseMax = true; Number.UIConfigInfo.Range.bUseMin = true; Number.UIConfigInfo.Range.MinValue = 0.0f; Number.UIConfigInfo.Range.MaxValue = 100.0f; Number.UIConfigInfo.DefaultChangeAmount = 1; Number.UIConfigInfo.MaxChangeAmount = 10; ConsoleCommandNumbers.Add(Number); Number.Title = FText::FromString(TEXT("RPC Debug")); Number.Description = FText::FromString(TEXT("すべてのRPC Bunchをログ表示(1=有効、0=無効)")); Number.PreConsoleCommandName = TEXT("net."); Number.ConsoleCommandName = TEXT("RPC"); Number.PostConsoleCommandName = TEXT(".Debug "); Number.ConsoleVariableName = TEXT("net.RPC.Debug"); Number.DefaultValue = 0.0f; Number.CommandNetType = EGDMConsoleCommandNetType::LocalOnly; Number.UIConfigInfo.Range.bUseMax = true; Number.UIConfigInfo.Range.bUseMin = true; Number.UIConfigInfo.Range.MinValue = 0.0f; Number.UIConfigInfo.Range.MaxValue = 1.0f; Number.UIConfigInfo.DefaultChangeAmount = 1; Number.UIConfigInfo.MaxChangeAmount = 1; ConsoleCommandNumbers.Add(Number); Number.Title = FText::FromString(TEXT("Reliable Debug")); Number.Description = FText::FromString(TEXT("すべてのReliable Bunchのログ表示(0=出力なし、1=送信時のみ出力、2=更新時に出力)")); Number.PreConsoleCommandName = TEXT("net."); Number.ConsoleCommandName = TEXT("Reliable"); Number.PostConsoleCommandName = TEXT(".Debug "); Number.ConsoleVariableName = TEXT("net.Reliable.Debug"); Number.DefaultValue = 0.0f; Number.CommandNetType = EGDMConsoleCommandNetType::LocalOnly; Number.UIConfigInfo.Range.bUseMax = true; Number.UIConfigInfo.Range.bUseMin = true; Number.UIConfigInfo.Range.MinValue = 0.0f; Number.UIConfigInfo.Range.MaxValue = 2.0f; Number.UIConfigInfo.DefaultChangeAmount = 1; Number.UIConfigInfo.MaxChangeAmount = 1; ConsoleCommandNumbers.Add(Number); Number.Title = FText::FromString(TEXT("NetShowCorrections")); Number.Description = FText::FromString(TEXT("クライアント (またはサーバーへ送信) がネットワーク修正を受け取った時期を確認できるようにする(1=有効、0=無効)")); Number.PreConsoleCommandName = TEXT("p."); Number.ConsoleCommandName = TEXT("NetShowCorrections"); Number.PostConsoleCommandName = TEXT(" "); Number.ConsoleVariableName = TEXT("p.NetShowCorrections"); Number.DefaultValue = 0.0f; Number.CommandNetType = EGDMConsoleCommandNetType::LocalOnly; Number.UIConfigInfo.Range.bUseMax = true; Number.UIConfigInfo.Range.bUseMin = true; Number.UIConfigInfo.Range.MinValue = 0.0f; Number.UIConfigInfo.Range.MaxValue = 1.0f; Number.UIConfigInfo.DefaultChangeAmount = 1; Number.UIConfigInfo.MaxChangeAmount = 1; ConsoleCommandNumbers.Add(Number); Number.Title = FText::FromString(TEXT("Debug Draw")); Number.Description = FText::FromString(TEXT("ネットワークのdormancyとrelevancyの情報を表示(1=有効、0=無効)")); Number.PreConsoleCommandName = TEXT("net."); Number.ConsoleCommandName = TEXT("DebugDraw"); Number.PostConsoleCommandName = TEXT(" "); Number.ConsoleVariableName = TEXT("net.DebugDraw"); Number.DefaultValue = 0.0f; Number.CommandNetType = EGDMConsoleCommandNetType::LocalOnly; Number.UIConfigInfo.Range.bUseMax = true; Number.UIConfigInfo.Range.bUseMin = true; Number.UIConfigInfo.Range.MinValue = 0.0f; Number.UIConfigInfo.Range.MaxValue = 1.0f; Number.UIConfigInfo.DefaultChangeAmount = 1; Number.UIConfigInfo.MaxChangeAmount = 1; ConsoleCommandNumbers.Add(Number); Number.Title = FText::FromString(TEXT("Context Debug")); Number.Description = FText::FromString(TEXT("Replication情報にデバッグ用文字列を設定してログに詳細な情報を表示(1=有効、0=無効)")); Number.PreConsoleCommandName = TEXT("net."); Number.ConsoleCommandName = TEXT("ContextDebug"); Number.PostConsoleCommandName = TEXT(" "); Number.ConsoleVariableName = TEXT("net.ContextDebug"); Number.DefaultValue = 0.0f; Number.CommandNetType = EGDMConsoleCommandNetType::LocalOnly; Number.UIConfigInfo.Range.bUseMax = true; Number.UIConfigInfo.Range.bUseMin = true; Number.UIConfigInfo.Range.MinValue = 0.0f; Number.UIConfigInfo.Range.MaxValue = 1.0f; Number.UIConfigInfo.DefaultChangeAmount = 1; Number.UIConfigInfo.MaxChangeAmount = 1; ConsoleCommandNumbers.Add(Number); Number.Title = FText::FromString(TEXT("Dormancy Enable")); Number.Description = FText::FromString(TEXT("頻繁に更新されるアクターのCPUおよび帯域幅のオーバーヘッドを削減するネットワーク休止システムの有効化(1=有効、0=無効)")); Number.PreConsoleCommandName = TEXT("net."); Number.ConsoleCommandName = TEXT("DormancyEnable"); Number.PostConsoleCommandName = TEXT(" "); Number.ConsoleVariableName = TEXT("net.DormancyEnable"); Number.DefaultValue = 1.0f; Number.CommandNetType = EGDMConsoleCommandNetType::LocalOnly; Number.UIConfigInfo.Range.bUseMax = true; Number.UIConfigInfo.Range.bUseMin = true; Number.UIConfigInfo.Range.MinValue = 0.0f; Number.UIConfigInfo.Range.MaxValue = 1.0f; Number.UIConfigInfo.DefaultChangeAmount = 1; Number.UIConfigInfo.MaxChangeAmount = 1; ConsoleCommandNumbers.Add(Number); Number.Title = FText::FromString(TEXT("Debug Draw CullDistance")); Number.Description = FText::FromString(TEXT("localのViewから離れているActorの休止状態を描画する\nこの距離(World単位)より近いものが描画対象(net.DebugDrawが有効かつ 0 以上指定で動作)")); Number.PreConsoleCommandName = TEXT("net."); Number.ConsoleCommandName = TEXT("DebugDrawCullDistance"); Number.PostConsoleCommandName = TEXT(" "); Number.ConsoleVariableName = TEXT("net.DebugDrawCullDistance"); Number.DefaultValue = 0.0f; Number.CommandNetType = EGDMConsoleCommandNetType::LocalOnly; Number.UIConfigInfo.Range.bUseMax = false; Number.UIConfigInfo.Range.bUseMin = true; Number.UIConfigInfo.Range.MinValue = 0.0f; Number.UIConfigInfo.Range.MaxValue = 0.0f; Number.UIConfigInfo.DefaultChangeAmount = 50; Number.UIConfigInfo.MaxChangeAmount = 100; ConsoleCommandNumbers.Add(Number); FGDMConsoleCommandSingle Single; Single.CategoryIndex = 10; /* Network */ Single.CommandNetType = EGDMConsoleCommandNetType::LocalOnly; Single.Title = FText::FromString(TEXT("NET")); Single.Description = FText::FromString(TEXT("ネットワーキング システムに関する統計情報を表示")); Single.ConsoleCommandName = TEXT("stat NET"); Single.Title = FText::FromString(TEXT("Online")); Single.Description = FText::FromString(TEXT("オンライン システムのカウンターを表示")); Single.ConsoleCommandName = TEXT("stat Online"); Single.Title = FText::FromString(TEXT("Dump Relevant Actors")); Single.Description = FText::FromString(TEXT("次回ネットワーク更新時に関連するActorの情報を出力")); Single.ConsoleCommandName = TEXT("net.DumpRelevantActors"); Single.Title = FText::FromString(TEXT("List Actor Channels")); Single.Description = FText::FromString(TEXT("ActorChannelのリスト一覧表示")); Single.ConsoleCommandName = TEXT("net.ListActorChannels"); Single.Title = FText::FromString(TEXT("List Net GUIDs")); Single.Description = FText::FromString(TEXT("NetGUIDのリスト一覧表示")); Single.ConsoleCommandName = TEXT("net.ListNetGUIDs"); Single.Title = FText::FromString(TEXT("List Net GUID Exports")); Single.Description = FText::FromString(TEXT("NetGUIDとエクスポート回数のリスト一覧表示")); Single.ConsoleCommandName = TEXT("net.ListNetGUIDExports"); } void UGameDebugMenuSettings::SetupCategorySound() { FGDMConsoleCommandSingle Single; Single.CategoryIndex = 11; /* Sounds */ Single.CommandNetType = EGDMConsoleCommandNetType::LocalOnly; FGDMConsoleCommandGroup Group; Group.CategoryIndex = 11; /* Sounds */ Group.CommandNetType = EGDMConsoleCommandNetType::LocalOnly; Group.Title = FText::FromString(TEXT("Sounds")); Group.Description = FText::FromString(TEXT("アクティブなサウンドキューとサウンドウェーブ、SoundMixeを表示")); Group.ConsoleCommandNames.Add("stat SoundMixes"); Group.ConsoleCommandNames.Add("stat SoundWaves"); Group.ConsoleCommandNames.Add("stat SoundCues"); Group.ConsoleCommandNames.Add("stat Sounds"); ConsoleCommandGroups.Add(Group); Group.ConsoleCommandNames.Empty(); } void UGameDebugMenuSettings::SetupCategoryAbilitySystem() { /* https://dev.epicgames.com/community/learning/tutorials/Y477/unreal-engine-gameplay-ability-system-debugging-tools */ FGDMConsoleCommandSingle Single; Single.CategoryIndex = 12; /* Ability System */ Single.CommandNetType = EGDMConsoleCommandNetType::LocalOnly; Single.Title = FText::FromString(TEXT("Show abilitysystem")); Single.Description = FText::FromString(TEXT("AbilitySystemのデバック表示をする")); Single.ConsoleCommandName = TEXT("showdebug abilitysystem"); ConsoleCommandNames.Add(Single); Single.Title = FText::FromString(TEXT("Close abilitysystem")); Single.Description = FText::FromString(TEXT("AbilitySystemの非表示にする")); Single.ConsoleCommandName = TEXT("showdebug"); ConsoleCommandNames.Add(Single); Single.Title = FText::FromString(TEXT("AbilitySystem NextCategory")); Single.Description = FText::FromString(TEXT("AbilitySystemのデバック表示がされてる場合表示内容(所持アビリティ、エフェクトなど)を切り替える")); Single.ConsoleCommandName = TEXT("abilitySystem.debug.nextCategory"); ConsoleCommandNames.Add(Single); Single.Title = FText::FromString(TEXT("AbilitySystem NextTarget")); Single.Description = FText::FromString(TEXT("AbilitySystemのデバック表示対象を変更する(次へ移る)")); // Single.ConsoleCommandName = TEXT("abilitysystem.debug.nexttarget"); Single.ConsoleCommandName = TEXT("NextDebugTarget"); ConsoleCommandNames.Add(Single); Single.Title = FText::FromString(TEXT("AbilitySystem PrevTarget")); Single.Description = FText::FromString(TEXT("AbilitySystemのデバック表示を対象を変更する(1つ戻る)")); // Single.ConsoleCommandName = TEXT("abilitysystem.debug.prevtarget"); Single.ConsoleCommandName = TEXT("PreviousDebugTarget"); ConsoleCommandNames.Add(Single); Single.Title = FText::FromString(TEXT("AbilitySystem DebugBasicHUD")); Single.Description = FText::FromString(TEXT("操作キャラのAttribute情報を表示")); Single.ConsoleCommandName = TEXT("AbilitySystem.DebugBasicHUD"); ConsoleCommandNames.Add(Single); Single.Title = FText::FromString(TEXT("AbilitySystem DebugAbilityTags")); Single.Description = FText::FromString(TEXT("AbilitySystemで所持するタグ情報をアクター位置にすべて表示")); Single.ConsoleCommandName = TEXT("AbilitySystem.DebugAbilityTags"); ConsoleCommandNames.Add(Single); Single.Title = FText::FromString(TEXT("AbilitySystem DebugAttribute")); Single.Description = FText::FromString(TEXT("AbilitySystemで所持するAttribute情報をアクター位置にすべて表示")); Single.ConsoleCommandName = TEXT("AbilitySystem.DebugAttribute"); ConsoleCommandNames.Add(Single); Single.Title = FText::FromString(TEXT("AbilitySystem ClearDebugAttributes")); Single.Description = FText::FromString(TEXT("AbilitySystemで所持するAttribute情報をクリアする")); Single.ConsoleCommandName = TEXT("AbilitySystem.ClearDebugAttributes"); ConsoleCommandNames.Add(Single); FGDMConsoleCommandNumber Number; Number.CategoryIndex = 12; /* Ability System */ Number.Title = FText::FromString(TEXT("AbilitySystem.DebugDrawMaxDistance")); Number.Description = FText::FromString(TEXT("アクター位置に表示するAbilitySystemのデバック情報の表示距離を設定する")); Number.PreConsoleCommandName = TEXT(""); Number.ConsoleCommandName = TEXT("AbilitySystem.DebugDrawMaxDistance"); Number.PostConsoleCommandName = TEXT(" "); Number.DefaultValue = 2048.0f; Number.ConsoleVariableName = TEXT("AbilitySystem.DebugDrawMaxDistance"); Number.CommandNetType = EGDMConsoleCommandNetType::LocalOnly; Number.UIConfigInfo.Range.bUseMax = false; Number.UIConfigInfo.Range.bUseMin = true; Number.UIConfigInfo.Range.MinValue = 10.0f; Number.UIConfigInfo.Range.MaxValue = 0.0f; Number.UIConfigInfo.DefaultChangeAmount = 10; Number.UIConfigInfo.MaxChangeAmount = 100; ConsoleCommandNumbers.Add(Number); } void UGameDebugMenuSettings::SetupCategoryOther() { FGDMConsoleCommandSingle Single; Single.CategoryIndex = 13; /* Other */ Single.CommandNetType = EGDMConsoleCommandNetType::LocalOnly; Single.ClickedEvent = EGDMConsoleCommandClickedEvent::Non; Single.Title = FText::FromString(TEXT("RestartLevel")); Single.Description = FText::FromString(TEXT("レベルの再読込み")); Single.ConsoleCommandName = TEXT("RestartLevel"); ConsoleCommandNames.Add(Single); Single.Title = FText::FromString(TEXT("ShowLog")); Single.Description = FText::FromString(TEXT("コンソールウィンドウの(非)表示する")); Single.ConsoleCommandName = TEXT("ShowLog"); ConsoleCommandNames.Add(Single); Single.Title = FText::FromString(TEXT("Try Garbage Collection")); Single.Description = FText::FromString(TEXT("ガーベジコレクションを即時実行し、定期実行タイマーをリセットする")); Single.ConsoleCommandName = TEXT("Obj trygc"); ConsoleCommandNames.Add(Single); Single.CommandNetType = EGDMConsoleCommandNetType::LocalOnly; Single.Title = FText::FromString(TEXT("Exit Game")); Single.Description = FText::FromString(TEXT("ゲームを即終了させる")); Single.ConsoleCommandName = TEXT("exit"); ConsoleCommandNames.Add(Single); FGDMConsoleCommandNumber Number; Number.CategoryIndex = 13; /* Other */ Number.Title = FText::FromString(TEXT("Change Max FPS")); Number.Description = FText::FromString(TEXT("FPSの変更")); Number.PreConsoleCommandName = TEXT("t."); Number.ConsoleCommandName = TEXT("MaxFPS"); Number.PostConsoleCommandName = TEXT(" "); Number.DefaultValue = 60.0f; Number.ConsoleVariableName = TEXT("t.MaxFPS"); Number.CommandNetType = EGDMConsoleCommandNetType::LocalOnly; Number.UIConfigInfo.Range.bUseMax = true; Number.UIConfigInfo.Range.bUseMin = true; Number.UIConfigInfo.Range.MinValue = 15.0f; Number.UIConfigInfo.Range.MaxValue = 200.0f; Number.UIConfigInfo.DefaultChangeAmount = 5; Number.UIConfigInfo.MaxChangeAmount = 10; ConsoleCommandNumbers.Add(Number); } void UGameDebugMenuSettings::SetupCategoryLogVerbosity() { /* Engine\Source\Runtime\Core\Public\Logging\LogVerbosity.hのELogVerbosity参照 */ FGDMConsoleCommandSingle Single; Single.CategoryIndex = 14; /* Log Verbosity */ Single.CommandNetType = EGDMConsoleCommandNetType::LocalOnly; /************************************************************************/ /* LogTemp */ /************************************************************************/ Single.Title = FText::FromString(TEXT("LogTemp - Display")); Single.Description = FText::FromString(TEXT("LogTempのVerbosityを「Display」に変更")); Single.ConsoleCommandName = TEXT("log LogTemp Display"); ConsoleCommandNames.Add(Single); Single.Title = FText::FromString(TEXT("LogTemp - Log")); Single.Description = FText::FromString(TEXT("LogTempのVerbosityを「Log」に変更")); Single.ConsoleCommandName = TEXT("log LogTemp Log"); ConsoleCommandNames.Add(Single); Single.Title = FText::FromString(TEXT("LogTemp - Verbose")); Single.Description = FText::FromString(TEXT("LogTempのVerbosityを「Verbose」に変更")); Single.ConsoleCommandName = TEXT("log LogTemp Verbose"); ConsoleCommandNames.Add(Single); Single.Title = FText::FromString(TEXT("LogTemp - VeryVerbose")); Single.Description = FText::FromString(TEXT("LogTempのVerbosityを「VeryVerbose」に変更")); Single.ConsoleCommandName = TEXT("log LogTemp VeryVerbose"); ConsoleCommandNames.Add(Single); /************************************************************************/ /* LogAbilitySystem */ /************************************************************************/ Single.Title = FText::FromString(TEXT("LogAbilitySystem - Display")); Single.Description = FText::FromString(TEXT("LogAbilitySystemのVerbosityを「Display」に変更")); Single.ConsoleCommandName = TEXT("log LogAbilitySystem Display"); ConsoleCommandNames.Add(Single); Single.Title = FText::FromString(TEXT("LogAbilitySystem - Log")); Single.Description = FText::FromString(TEXT("LogAbilitySystemのVerbosityを「Log」に変更")); Single.ConsoleCommandName = TEXT("log LogAbilitySystem Log"); ConsoleCommandNames.Add(Single); Single.Title = FText::FromString(TEXT("LogAbilitySystem - Verbose")); Single.Description = FText::FromString(TEXT("LogAbilitySystemのVerbosityを「Verbose」に変更")); Single.ConsoleCommandName = TEXT("log LogAbilitySystem Verbose"); ConsoleCommandNames.Add(Single); Single.Title = FText::FromString(TEXT("LogAbilitySystem - VeryVerbose")); Single.Description = FText::FromString(TEXT("LogAbilitySystemのVerbosityを「VeryVerbose」に変更")); Single.ConsoleCommandName = TEXT("log LogAbilitySystem VeryVerbose"); ConsoleCommandNames.Add(Single); } ``` `Plugin/GameDebugMenu/Source/GameDebugMenu/Private/GameDebugMenuTypes.cpp`: ```cpp /** * Copyright (c) 2020 akihiko moroi * * This software is released under the MIT License. * (See accompanying file LICENSE.txt or copy at http://opensource.org/licenses/MIT) */ #include "GameDebugMenuTypes.h" DEFINE_LOG_CATEGORY(LogGDM) /************************************************************************ * FGDMConsoleCommand ************************************************************************/ FGDMConsoleCommand::FGDMConsoleCommand() { CategoryIndex = 255; Title = FText::GetEmpty(); Description = FText::GetEmpty(); Type = EGDMConsoleCommandType::Non; ClickedEvent = EGDMConsoleCommandClickedEvent::Non; CommandNetType = EGDMConsoleCommandNetType::LocalOnly; } FGDMConsoleCommandSingle::FGDMConsoleCommandSingle() : Super() { ConsoleCommandName.Empty(); Type = EGDMConsoleCommandType::Single; } FString FGDMConsoleCommandSingle::BuildCommandIdentifier() const { return FString::Printf(TEXT("Single_%s"), *ConsoleCommandName.Replace(TEXT(" "), TEXT("_"))); } FGDMConsoleCommandGroup::FGDMConsoleCommandGroup() : Super() { ConsoleCommandNames.Reset(); Type = EGDMConsoleCommandType::Group; } FString FGDMConsoleCommandGroup::BuildCommandIdentifier() const { const FString Joined = FString::Join(ConsoleCommandNames, TEXT("_")); return FString::Printf(TEXT("Group_%s"), *Joined.Replace(TEXT(" "), TEXT("_"))); } FGDMConsoleCommandPair::FGDMConsoleCommandPair() : Super() { FirstConsoleCommandName.Empty(); SecondConsoleCommandName.Empty(); Type = EGDMConsoleCommandType::Pair; } FString FGDMConsoleCommandPair::BuildCommandIdentifier() const { return FString::Printf(TEXT("Pair_%s_TO_%s"), *FirstConsoleCommandName.Replace(TEXT(" "), TEXT("_")), *SecondConsoleCommandName.Replace(TEXT(" "), TEXT("_")) ); } FGDMConsoleCommandNumber::FGDMConsoleCommandNumber() : Super() { ConsoleCommandName.Empty(); PreConsoleCommandName.Empty(); PostConsoleCommandName.Empty(); Type = EGDMConsoleCommandType::Number; UIConfigInfo.Range.bUseMax = true; UIConfigInfo.Range.bUseMin = true; UIConfigInfo.Range.MinValue = 0.0f; UIConfigInfo.Range.MaxValue = 1.0f; DefaultValue = 0.0f; ConsoleVariableName.Empty(); } FString FGDMConsoleCommandNumber::BuildCommandIdentifier() const { return FString::Printf(TEXT("Number_%s_Default%.2f"), *ConsoleCommandName.Replace(TEXT(" "), TEXT("_")), DefaultValue ); } /************************************************************************ * FGDMJiraSettings ************************************************************************/ FString FGDMJiraSettings::GetAssigneeAccountIdByListIndex(int32 ListIndex) const { TArray AccountIds; AssigneeList.GenerateKeyArray(AccountIds); if(AccountIds.IsValidIndex(ListIndex)) { return AccountIds[ListIndex]; } return FString(); } FText FGDMJiraSettings::GetAssigneeTextByListIndex(int32 ListIndex) const { TArray TextList; AssigneeList.GenerateValueArray(TextList); if(TextList.IsValidIndex(ListIndex)) { return TextList[ListIndex]; } return FText(); } /************************************************************************ * FGDMMenuCategoryKey ************************************************************************/ FGDMMenuCategoryKey::FGDMMenuCategoryKey(uint8 InIndex, FString InKeyName) : Index(InIndex) , KeyName(InKeyName) { } FGDMMenuCategoryKey::FGDMMenuCategoryKey(uint8 InIndex) : Index(InIndex) , KeyName() { } FGDMMenuCategoryKey::FGDMMenuCategoryKey() : Index(0) { } bool FGDMMenuCategoryKey::operator==(FGDMMenuCategoryKey& InOther) { return InOther.Index == Index; } bool FGDMMenuCategoryKey::operator!=(FGDMMenuCategoryKey& InOther) { return !((*this) == InOther); } bool FGDMMenuCategoryKey::operator<(FGDMMenuCategoryKey& InOther) { return InOther.Index < Index; } bool FGDMMenuCategoryKey::operator>(FGDMMenuCategoryKey& InOther) { return InOther.Index > Index; } /************************************************************************ * FGDMGameplayCategoryKey ************************************************************************/ FGDMGameplayCategoryKey::FGDMGameplayCategoryKey(uint8 InKey, FString InKeyName) : Super(InKey,InKeyName) { } FGDMGameplayCategoryKey::FGDMGameplayCategoryKey(uint8 InKey) : Super(InKey) { } FGDMGameplayCategoryKey::FGDMGameplayCategoryKey() : Super() { } ``` `Plugin/GameDebugMenu/Source/GameDebugMenu/Private/Input/GDMDebugCameraInput.cpp`: ```cpp /** * Copyright (c) 2020 akihiko moroi * * This software is released under the MIT License. * (See accompanying file LICENSE.txt or copy at http://opensource.org/licenses/MIT) */ #include "Input/GDMDebugCameraInput.h" #include "Engine/World.h" #include "Engine/DebugCameraController.h" #include "GameDebugMenuManager.h" #include AGDMDebugCameraInput::AGDMDebugCameraInput() : Super() , DebugCameraController(nullptr) { PrimaryActorTick.bCanEverTick = false; PrimaryActorTick.bStartWithTickEnabled = false; } AGameDebugMenuManager* AGDMDebugCameraInput::GetOwnerGameDebugMenuManager() const { return Cast(GetOwner()); } ADebugCameraController* AGDMDebugCameraInput::GetDebugCameraController() const { return DebugCameraController.Get(); } void AGDMDebugCameraInput::SetDebugCameraController(ADebugCameraController* DCC) { DebugCameraController = DCC; } void AGDMDebugCameraInput::ToggleOrbitHitPoint() { UE_LOG(LogGDM, Log, TEXT("AGDMDebugCameraInput::ToggleOrbitHitPoint Call")); AGameDebugMenuManager* DebugMenuManager = GetOwnerGameDebugMenuManager(); if (!IsValid(DebugMenuManager)) { return; } if (DebugMenuManager->IsInputIgnored()) { return; } if(DebugCameraController.IsValid()) { DebugCameraController->ToggleOrbitHitPoint(); } } void AGDMDebugCameraInput::PawnTeleport() { UE_LOG(LogGDM, Log, TEXT("AGDMDebugCameraInput::PawnTeleport Call")); AGameDebugMenuManager* DebugMenuManager = GetOwnerGameDebugMenuManager(); if (!IsValid(DebugMenuManager)) { return; } if (DebugMenuManager->IsInputIgnored()) { return; } DebugMenuManager->ExecuteConsoleCommand(TEXT("Teleport"), DebugMenuManager->GetOwnerPlayerController()); } ``` `Plugin/GameDebugMenu/Source/GameDebugMenu/Private/Input/GDMEnhancedInputComponent.cpp`: ```cpp /** * Copyright (c) 2020 akihiko moroi * * This software is released under the MIT License. * (See accompanying file LICENSE.txt or copy at http://opensource.org/licenses/MIT) */ #include "Input/GDMEnhancedInputComponent.h" #include "GameDebugMenuFunctions.h" #include "GameDebugMenuSettings.h" UGDMEnhancedInputComponent::UGDMEnhancedInputComponent() { PrimaryComponentTick.bCanEverTick = false; PrimaryComponentTick.bStartWithTickEnabled = false; /* 最後に追加されたUI用InputComponentだけ処理したい(アンバインド処理をしなくてもいいように)のでブロック指定にする */ bBlockInput = true; /* DebugMenuはEnhancedInputを利用するので他のInputComponentより優先度は高くなるような値を指定 */ Priority = GetDefault()->WidgetInputActionPriority; } bool UGDMEnhancedInputComponent::CanProcessInputAction(const UInputAction* Action) const { AGameDebugMenuManager* Manager = UGameDebugMenuFunctions::GetGameDebugMenuManager(this); if (!IsValid(Manager)) { return false; } if (Manager->IsInputIgnored()) { return false; } return true; } FEnhancedInputActionEventBinding& UGDMEnhancedInputComponent::BindDebugMenuAction(const UInputAction* Action, ETriggerEvent TriggerEvent, UObject* Object, FName FunctionName) { return BindActionInstanceLambda( Action, TriggerEvent, [this, Object, FunctionName, Action](const FInputActionInstance& Instance) { if (CanProcessInputAction(Action) && IsValid(Object)) { if (UFunction* Func = Object->FindFunction(FunctionName)) { Object->ProcessEvent(Func, nullptr); } } }); } FEnhancedInputActionEventBinding& UGDMEnhancedInputComponent::BindDebugMenuAction(const UInputAction* Action, ETriggerEvent TriggerEvent, TFunction&& InCallback) { return BindActionInstanceLambda( Action, TriggerEvent, [this, Action, Callback = MoveTemp(InCallback)](const FInputActionInstance& Instance) { if (CanProcessInputAction(Action)) { Callback(Instance); } }); } ``` `Plugin/GameDebugMenu/Source/GameDebugMenu/Private/Input/GDMInputSystemComponent.cpp`: ```cpp /** * Copyright (c) 2020 akihiko moroi * * This software is released under the MIT License. * (See accompanying file LICENSE.txt or copy at http://opensource.org/licenses/MIT) */ #include "Input/GDMInputSystemComponent.h" #include #include "EnhancedInputSubsystems.h" #include "Engine/DebugCameraController.h" #include "GameFramework/CheatManager.h" #include "GameDebugMenuFunctions.h" #include "GameDebugMenuManager.h" #include "Component/GDMLocalizeStringComponent.h" #include "Input/GDMDebugCameraInput.h" #include "Input/GDMEnhancedInputComponent.h" #include "Widgets/GameDebugMenuRootWidget.h" UGDMInputSystemComponent::UGDMInputSystemComponent() : IgnoreDebugMenuInput(0) , DebugCameraInput(nullptr) , RegisteredInputGroups() , ActiveInputStacks() , CurrentInputGroupName(NAME_None) , bMenuOpen(false) , RootWidgetInputComponent(nullptr) , AddInputMappingContextWhenCreateManager() , AddInputMappingContextWhenDebugMenuIsShow() , ActorSpawnedDelegateHandle() , DebugCameraController(nullptr) , bOutputDebugLog(false) { PrimaryComponentTick.bCanEverTick = true; PrimaryComponentTick.bStartWithTickEnabled = true; PrimaryComponentTick.TickGroup = ETickingGroup::TG_PrePhysics; PrimaryComponentTick.bTickEvenWhenPaused = true; bNeverNeedsRenderUpdate = true; } void UGDMInputSystemComponent::TickComponent(float DeltaTime, enum ELevelTick TickType, FActorComponentTickFunction* ThisTickFunction) { Super::TickComponent(DeltaTime, TickType, ThisTickFunction); if (bOutputDebugLog) { FString DebugText; const AGameDebugMenuManager* Manager = GetOwnerGameDebugMenuManager(); FString GroupName; Manager->GetLocalizeStringComponent()->GetString(CurrentInputGroupName.ToString(), GroupName); DebugText += FString::Printf(TEXT("[GameDebugMenu] Input Current Group: %s\n"), *GroupName); if (const TArray>* Stack = ActiveInputStacks.Find(CurrentInputGroupName)) { for (const TWeakObjectPtr& Comp : *Stack) { if (const UInputComponent* Input = Comp.Get()) { const UObject* Outer = Input->GetOuter(); FString OuterLabel = TEXT("(null)"); if (IsValid(Outer)) { FString AssetName; Outer->GetFullName(nullptr).Split(TEXT("."), nullptr, &AssetName, ESearchCase::IgnoreCase, ESearchDir::FromEnd); OuterLabel = AssetName; } DebugText += FString::Printf(TEXT(" > %s\n"), *OuterLabel); } } } GEngine->AddOnScreenDebugMessage(-1, 0.0f, FColor::Green, DebugText); } } void UGDMInputSystemComponent::EndPlay(const EEndPlayReason::Type EndPlayReason) { Super::EndPlay(EndPlayReason); if(IsValid(DebugCameraInput)) { DebugCameraInput->K2_DestroyActor(); DebugCameraInput = nullptr; } } void UGDMInputSystemComponent::SetIgnoreInput(bool bNewInput) { IgnoreDebugMenuInput = FMath::Max(IgnoreDebugMenuInput + (bNewInput ? +1 : -1), 0); } void UGDMInputSystemComponent::ResetIgnoreInput() { IgnoreDebugMenuInput = 0; } bool UGDMInputSystemComponent::IsInputIgnored() const { return (IgnoreDebugMenuInput > 0); } void UGDMInputSystemComponent::Initialize(UGameDebugMenuManagerAsset* MenuDataAsset) { AddInputMappingContextWhenCreateManager = MenuDataAsset->AddInputMappingContextWhenCreateManager; AddInputMappingContextWhenDebugMenuIsShow = MenuDataAsset->AddInputMappingContextWhenDebugMenuIsShow; CreateDebugCameraInputClass(MenuDataAsset->DebugCameraInputClass); TArray PCs = GetPlayerControllers(); for (APlayerController* PC : PCs) { if (UEnhancedInputLocalPlayerSubsystem* Subsystem = ULocalPlayer::GetSubsystem(PC->GetLocalPlayer())) { for (const auto& Data : AddInputMappingContextWhenCreateManager) { if (!Subsystem->HasMappingContext(Data.InputMappingContext)) { Subsystem->AddMappingContext(Data.InputMappingContext, Data.Priority); } } } } if (UGameDebugMenuRootWidget* Root = GetOwnerGameDebugMenuManager()->GetDebugMenuRootWidget()) { Root->EnsureDebugMenuInputComponent(); RootWidgetInputComponent = Root->GetMyInputComponent(); } } void UGDMInputSystemComponent::RegisterInputComponent(UInputComponent* InputComponent) { /* 現在指定されたグループに対してコンポーネントを処理する */ RegisterInputComponentToGroup(InputComponent, CurrentInputGroupName); } void UGDMInputSystemComponent::UnregisterInputComponent(UInputComponent* InputComponent) { /* 現在指定されたグループに対してコンポーネントを処理する */ UnregisterInputComponentFromGroup(InputComponent, CurrentInputGroupName); } void UGDMInputSystemComponent::RegisterInputComponentToGroup(UInputComponent* InputComponent, const FName GroupName) { if ( !IsValid(InputComponent) ) { UE_LOG(LogGDM, Verbose, TEXT("RegisterInputComponent failed: Not found InputComponent")); return; } if ( InputComponent == RootWidgetInputComponent ) { UE_LOG(LogGDM, Verbose, TEXT("RegisterInputComponent failed: RootWidgetInputComponent")); return; } if (GroupName.IsNone()) { UE_LOG(LogGDM, Warning, TEXT("RegisterInputComponent failed: GroupName is None")); return; } TArray>& Group = RegisteredInputGroups.FindOrAdd(GroupName); if (Group.Contains(InputComponent)) { /* 既に登録済みであればその場合、なにもしない */ return; } Group.Add(InputComponent); if (GroupName == CurrentInputGroupName && bMenuOpen) { /* 現在のグループを指定し、メニューが開いていればコントローラーに追加 */ TArray PCs = GetPlayerControllers(); for (APlayerController* PC : PCs) { PC->PushInputComponent(InputComponent); } ActiveInputStacks.FindOrAdd(GroupName).AddUnique(InputComponent); } } void UGDMInputSystemComponent::UnregisterInputComponentFromGroup(UInputComponent* InputComponent, const FName GroupName) { if ( !IsValid(InputComponent) ) { UE_LOG(LogGDM, Verbose, TEXT("UnregisterInputComponent failed: Not found InputComponent")); return; } if (InputComponent == RootWidgetInputComponent) { UE_LOG(LogGDM, Verbose, TEXT("UnregisterInputComponent failed: RootWidgetInputComponent")); return; } if (GroupName.IsNone()) { UE_LOG(LogGDM, Warning, TEXT("UnregisterInputComponent failed: GroupName is None")); return; } if (RegisteredInputGroups.Contains(GroupName)) { RegisteredInputGroups[GroupName].Remove(InputComponent); } if (ActiveInputStacks.Contains(GroupName)) { if (ActiveInputStacks[GroupName].Remove(InputComponent) > 0) { TArray PCs = GetPlayerControllers(); for (APlayerController* PC : PCs) { PC->PopInputComponent(InputComponent); } } } } void UGDMInputSystemComponent::SwitchToInputGroup(const FName NewGroupName) { if (NewGroupName == CurrentInputGroupName) { return; } TArray PCs = GetPlayerControllers(); /* 現在のグループのInputComponentを除外 */ if (TArray>* Stack = ActiveInputStacks.Find(CurrentInputGroupName)) { for (const auto& Comp : *Stack) { if (Comp.IsValid()) { for (APlayerController* PC : PCs) { PC->PopInputComponent(Comp.Get()); } } } Stack->Reset(); } CurrentInputGroupName = NewGroupName; /* 新しいグループのInputComponentを追加 */ if (const TArray>* NewStack = RegisteredInputGroups.Find(NewGroupName)) { for (const auto& Comp : *NewStack) { if (Comp.IsValid()) { for (APlayerController* PC : PCs) { PC->PushInputComponent(Comp.Get()); } ActiveInputStacks.FindOrAdd(NewGroupName).AddUnique(Comp); } } } } void UGDMInputSystemComponent::OnOpenMenu() { if (bMenuOpen) { return; } bMenuOpen = true; const TArray>* Group = RegisteredInputGroups.Find(CurrentInputGroupName); if (Group != nullptr) { for (const auto& Comp : *Group) { if (Comp.IsValid()) { ActiveInputStacks.FindOrAdd(CurrentInputGroupName).AddUnique(Comp); } } } TArray PCs = GetPlayerControllers(); for (APlayerController* PC : PCs) { if(UEnhancedInputLocalPlayerSubsystem* Subsystem = ULocalPlayer::GetSubsystem(PC->GetLocalPlayer())) { for (const auto& Data : AddInputMappingContextWhenDebugMenuIsShow) { if (!Subsystem->HasMappingContext(Data.InputMappingContext)) { Subsystem->AddMappingContext(Data.InputMappingContext, Data.Priority); } } } if (IsValid(RootWidgetInputComponent)) { PC->PushInputComponent(RootWidgetInputComponent); } if (Group != nullptr) { for (const auto& Comp : *Group) { if (Comp.IsValid()) { PC->PushInputComponent(Comp.Get()); } } } } } void UGDMInputSystemComponent::OnCloseMenu() { if (!bMenuOpen) { return; } bMenuOpen = false; TArray>* Stack = ActiveInputStacks.Find(CurrentInputGroupName); TArray PCs = GetPlayerControllers(); for (APlayerController* PC : PCs) { if(UEnhancedInputLocalPlayerSubsystem* Subsystem = ULocalPlayer::GetSubsystem(PC->GetLocalPlayer())) { for (const auto& Data : AddInputMappingContextWhenDebugMenuIsShow) { Subsystem->RemoveMappingContext(Data.InputMappingContext); } } if (Stack != nullptr) { for (const auto& Comp : *Stack) { if (Comp.IsValid()) { PC->PopInputComponent(Comp.Get()); } } } if (IsValid(RootWidgetInputComponent)) { PC->PopInputComponent(RootWidgetInputComponent); } } if (Stack != nullptr) { Stack->Reset(); } } void UGDMInputSystemComponent::CreateDebugCameraInputClass(TSubclassOf DebugCameraInputClass) { if(!IsValid(DebugCameraInputClass)) { return; } FActorSpawnParameters SpawnInfo; SpawnInfo.Owner = GetOwner(); SpawnInfo.SpawnCollisionHandlingOverride = ESpawnActorCollisionHandlingMethod::AlwaysSpawn; DebugCameraInput = GetWorld()->SpawnActor(DebugCameraInputClass, SpawnInfo); bool bExistDCC = false; const APlayerController* PC = GetOwnerGameDebugMenuManager()->GetOwnerPlayerController(); if(IsValid(PC->CheatManager)) { if(ADebugCameraController* DCC = PC->CheatManager->DebugCameraControllerRef) { bExistDCC = true; DebugCameraInput->EnableInput(DCC); } } if(!bExistDCC) { /* ないので生成するのを待つ */ ActorSpawnedDelegateHandle = GetWorld()->AddOnActorSpawnedHandler(FOnActorSpawned::FDelegate::CreateUObject(this, &UGDMInputSystemComponent::OnActorSpawned)); } } AGameDebugMenuManager* UGDMInputSystemComponent::GetOwnerGameDebugMenuManager() const { return Cast(GetOwner()); } TArray UGDMInputSystemComponent::GetPlayerControllers() const { TArray ReturnValues; if (APlayerController* TargetPC = GetOwnerGameDebugMenuManager()->GetOwnerPlayerController()) { ReturnValues.Add(TargetPC); if (IsValid(TargetPC->CheatManager)) { if (ADebugCameraController* DCC = TargetPC->CheatManager->DebugCameraControllerRef) { /* デバックカメラ側も対象に含める */ ReturnValues.Add(DCC); } } } return ReturnValues; } void UGDMInputSystemComponent::OnActorSpawned(AActor* SpawnActor) { DebugCameraController = Cast(SpawnActor); if (!DebugCameraController.IsValid()) { return; } GetWorld()->RemoveOnActorSpawnedHandler(ActorSpawnedDelegateHandle); DebugCameraInput->SetDebugCameraController(DebugCameraController.Get()); /* DebugCamera操作中にメニュー操作できるように有効化 */ { DebugCameraInput->EnableInput(DebugCameraController.Get()); GetOwnerGameDebugMenuManager()->EnableInput(DebugCameraController.Get()); } /* 生成直後はローカルプレイヤーが取得できないので次回フレームに */ TWeakObjectPtr WeakThis = this; GetWorld()->GetTimerManager().SetTimerForNextTick(FTimerDelegate::CreateWeakLambda(this, [WeakThis] { if(WeakThis.IsValid()) { if(UEnhancedInputLocalPlayerSubsystem* Subsystem = ULocalPlayer::GetSubsystem(WeakThis->DebugCameraController->GetLocalPlayer())) { for (const auto& Data : WeakThis->AddInputMappingContextWhenCreateManager) { if (!Subsystem->HasMappingContext(Data.InputMappingContext)) { Subsystem->AddMappingContext(Data.InputMappingContext, Data.Priority); } } } } })); } ``` `Plugin/GameDebugMenu/Source/GameDebugMenu/Private/Input/GDMInputTriggerPulseWithDelay.cpp`: ```cpp /** * Copyright (c) 2020 akihiko moroi * * This software is released under the MIT License. * (See accompanying file LICENSE.txt or copy at http://opensource.org/licenses/MIT) */ #include "Input/GDMInputTriggerPulseWithDelay.h" #include "EnhancedPlayerInput.h" FString UGDMInputTriggerPulseWithDelay::GetDebugState() const { const float TimeSinceFirst = FMath::Max(0.f, HeldDuration - InitialDelay); const float TimeToNext = (TriggerCount == 0) ? InitialDelay - HeldDuration : RepeatInterval - FMath::Fmod(TimeSinceFirst, RepeatInterval); return FString::Printf(TEXT("Held:%.2f Triggers:%d TimeToNext:%.2f"), HeldDuration, TriggerCount, TimeToNext); } ETriggerState UGDMInputTriggerPulseWithDelay::UpdateState_Implementation(const UEnhancedPlayerInput* PlayerInput, FInputActionValue ModifiedValue, float DeltaTime) { ETriggerState State = ETriggerState::None; // Transition to Ongoing on actuation. Update the held duration. if (IsActuated(ModifiedValue)) { State = ETriggerState::Ongoing; HeldDuration = CalculateHeldDuration(PlayerInput, DeltaTime); } else { // Reset duration HeldDuration = 0.0f; } if (State == ETriggerState::None) { bWasPressed = false; bTriggeredOnStart = false; TriggerCount = 0; return ETriggerState::None; } /* 初回押下処理 */ if (!bWasPressed) { bWasPressed = true; if (bTriggerOnStart) { bTriggeredOnStart = true; ++TriggerCount; return ETriggerState::Triggered; } } /* トリガー制限チェック */ if (TriggerLimit > 0 && TriggerCount >= TriggerLimit) { return ETriggerState::None; } /* 初回トリガー(押下後 InitialDelay 経過) */ if (!bTriggeredOnStart && TriggerCount == 0 && HeldDuration >= InitialDelay) { ++TriggerCount; return ETriggerState::Triggered; } /* 2回目以降(RepeatInterval ごと) */ if (TriggerCount > 0) { const float TimeSinceFirst = HeldDuration - InitialDelay; const int32 ExpectedCount = FMath::FloorToInt(TimeSinceFirst / RepeatInterval) + 1; if (ExpectedCount > TriggerCount) { ++TriggerCount; return ETriggerState::Triggered; } } return ETriggerState::Ongoing; } float UGDMInputTriggerPulseWithDelay::CalculateHeldDuration(const UEnhancedPlayerInput* const PlayerInput, const float DeltaTime) const { // We may not have a PlayerInput object during automation tests, so default to 1.0f if we don't have one. // This will mean that TimeDilation has no effect. const float TimeDilation = PlayerInput ? PlayerInput->GetEffectiveTimeDilation() : 1.0f; // Calculates the new held duration, applying time dilation if desired return HeldDuration + (!bAffectedByTimeDilation ? DeltaTime : DeltaTime * TimeDilation); } ``` `Plugin/GameDebugMenu/Source/GameDebugMenu/Private/Input/GDMPadInputWidgetController.cpp`: ```cpp /** * Copyright (c) 2020 akihiko moroi * * This software is released under the MIT License. * (See accompanying file LICENSE.txt or copy at http://opensource.org/licenses/MIT) */ #include "Input/GDMPadInputWidgetController.h" #include #include "Engine/Engine.h" UWorld* UGDMPadInputWidgetController::GetWorld() const { if( !IsValid(OwnerGameDebugMenuWidget) ) { return GWorld; } return OwnerGameDebugMenuWidget->GetWorld(); } ``` `Plugin/GameDebugMenu/Source/GameDebugMenu/Private/Log/GDMOutputDevice.cpp`: ```cpp /** * Copyright (c) 2020 akihiko moroi * * This software is released under the MIT License. * (See accompanying file LICENSE.txt or copy at http://opensource.org/licenses/MIT) */ #include "Log/GDMOutputDevice.h" #include "HAL/CriticalSection.h" #include #include #include "GameDebugMenuSettings.h" FGDMOutputDevice::FGDMOutputDevice() : FOutputDevice() , Logs() { Logs.Reserve(10000); CommandHistory.Reserve(100); GLog->AddOutputDevice(this); } FGDMOutputDevice::~FGDMOutputDevice() { if(GLog != nullptr) { GLog->RemoveOutputDevice(this); } } void FGDMOutputDevice::Serialize(const TCHAR* Data, ELogVerbosity::Type Verbosity, const class FName& Category, const double Time) { this->Serialize(Data, Verbosity, Category); } void FGDMOutputDevice::Serialize(const TCHAR* Data, ELogVerbosity::Type Verbosity, const class FName& Category) { if(Verbosity == ELogVerbosity::SetColor) { // Skip Color Events return; } else { static FName CommandCategory = TEXT("Cmd"); if (Category == CommandCategory) { while(CommandHistory.Num() > GetDefault()->MaxCommandHistoryNum - 1) { CommandHistory.RemoveAt(0); } bool bAdd = true; const FString CheckStr = Data; for (const auto& C : GetDefault()->NoSaveConsoleCommands) { if (CheckStr.Contains(C)) { bAdd = false; break; } } if (bAdd) { CommandHistory.Add(Data); } } /* 時間はUTCで固定し他はエディターの「outputlog」のものと同じものを保持しとく */ static ELogTimes::Type LogTimestampMode = ELogTimes::UTC; // handle multiline strings by breaking them apart by line TArray LineRanges; FString CurrentLogDump = Data; FTextRange::CalculateLineRangesFromString(CurrentLogDump, LineRanges); bool bIsFirstLineInMessage = true; for(const FTextRange& LineRange : LineRanges) { if(!LineRange.IsEmpty()) { FString Line = CurrentLogDump.Mid(LineRange.BeginIndex, LineRange.Len()); Line = Line.ConvertTabsToSpaces(4); // Hard-wrap lines to avoid them being too long static const int32 HardWrapLen = 360; for(int32 CurrentStartIndex = 0; CurrentStartIndex < Line.Len();) { int32 HardWrapLineLen = 0; if(bIsFirstLineInMessage) { FString MessagePrefix = FOutputDeviceHelper::FormatLogLine(Verbosity, Category, nullptr, LogTimestampMode); HardWrapLineLen = FMath::Min(HardWrapLen - MessagePrefix.Len(), Line.Len() - CurrentStartIndex); FString HardWrapLine = Line.Mid(CurrentStartIndex, HardWrapLineLen); Logs.Add(MessagePrefix + HardWrapLine); } else { HardWrapLineLen = FMath::Min(HardWrapLen, Line.Len() - CurrentStartIndex); FString HardWrapLine = Line.Mid(CurrentStartIndex, HardWrapLineLen); Logs.Add(MoveTemp(HardWrapLine)); } bIsFirstLineInMessage = false; CurrentStartIndex += HardWrapLineLen; } } } } } TArray FGDMOutputDevice::GetLogs() const { FScopeLock Lock(&CommandHistoryMutex); return Logs; } TArray FGDMOutputDevice::GetCommandHistory() const { FScopeLock Lock(&CommandHistoryMutex); return CommandHistory; } void FGDMOutputDevice::ClearCommandHistory() { FScopeLock Lock(&CommandHistoryMutex); CommandHistory.Reset(); } ``` `Plugin/GameDebugMenu/Source/GameDebugMenu/Private/Reports/GDMDebugReportRequester.cpp`: ```cpp /** * Copyright (c) 2020 akihiko moroi * * This software is released under the MIT License. * (See accompanying file LICENSE.txt or copy at http://opensource.org/licenses/MIT) */ #include "Reports/GDMDebugReportRequester.h" #include #include #include "Kismet/GameplayStatics.h" const FString AGDMDebugReportRequester::LineBreak = TEXT("\r\n"); AGDMDebugReportRequester::AGDMDebugReportRequester() : Super() , bSendLogs(false) , bSendScreenshotCapture(false) , Subject() , Description() , IssueCategoryIndex(0) , PriorityIndex(0) , AssigneeIndex(0) , ScreenshotImageData() , ScreenshotCapturedDateTime() , TestCount(0) , MaxTestCount(0) , bWasRequestSuccessful(false) { PrimaryActorTick.bCanEverTick = false; PrimaryActorTick.bStartWithTickEnabled = false; } void AGDMDebugReportRequester::StartRequest() { bWasRequestSuccessful = false; } void AGDMDebugReportRequester::SuccessRequest() { bWasRequestSuccessful = true; Destroy(); } void AGDMDebugReportRequester::FailedRequest() { bWasRequestSuccessful = false; Destroy(); } AGameDebugMenuManager* AGDMDebugReportRequester::GetOwnerDebugMenuManager() const { return Cast(GetOwner()); } FString AGDMDebugReportRequester::GetSubject() { return PrefixSubjectString() + Subject + SuffixSubjectString(); } FString AGDMDebugReportRequester::GetDescription() { return PrefixDescriptionString() + Description + SuffixDescriptionString(); } FString AGDMDebugReportRequester::PrefixSubjectString_Implementation() { return FString(); } FString AGDMDebugReportRequester::PrefixDescriptionString_Implementation() { return FString(); } FString AGDMDebugReportRequester::SuffixSubjectString_Implementation() { return FString(); } FString AGDMDebugReportRequester::SuffixDescriptionString_Implementation() { return FString::Printf(TEXT("\n\n====================\n Platform Name : %s\n Build Version : %s\n Build Configuration : %s\n Project Version : %s\n Test Count %d / %d\n") , *UGameplayStatics::GetPlatformName() , *UGameDebugMenuFunctions::GetGDMBuildVersionString() , *UGameDebugMenuFunctions::GetGDMBuildConfigurationString() , *UGameDebugMenuFunctions::GetGDMProjectVersionString() , TestCount, MaxTestCount ); } int32 AGDMDebugReportRequester::GetUTF8StringSize(const FString& Text) { int32 Size = 0; for(const TCHAR Char : Text) { const uint8 Code = static_cast(*TCHAR_TO_UTF8(*FString::Chr(Char))); if((Code >= 0x00) && (Code <= 0x7f)) { Size += 1; } else if((Code >= 0xc2) && (Code <= 0xdf)) { Size += 2; } else if((Code >= 0xe0) && (Code <= 0xef)) { Size += 3; } else if((Code >= 0xf0) && (Code <= 0xf7)) { Size += 4; } else if((Code >= 0xf8) && (Code <= 0xfb)) { Size += 5; } else if((Code >= 0xfc) && (Code <= 0xfd)) { Size += 6; } } return Size; } FString AGDMDebugReportRequester::MakeBoundaryString() { return FString::Printf(TEXT("ReportBoundary_%lld"), FDateTime::Now().ToUnixTimestamp()); } void AGDMDebugReportRequester::AddContentString(const FString& BoundaryKey, const FString& DataName, const FString& ContentType, const FString& SourceData, TArray& OutSendData) { FString WorkStr = LineBreak + TEXT("--") + BoundaryKey + LineBreak; OutSendData.Append((uint8*)TCHAR_TO_UTF8(*WorkStr), GetUTF8StringSize(WorkStr)); WorkStr = TEXT("Content-Disposition: form-data; ") + DataName + LineBreak; OutSendData.Append((uint8*)TCHAR_TO_UTF8(*WorkStr), GetUTF8StringSize(WorkStr)); WorkStr = TEXT("Content-Type: ") + ContentType + LineBreak + LineBreak; OutSendData.Append((uint8*)TCHAR_TO_UTF8(*WorkStr), GetUTF8StringSize(WorkStr)); OutSendData.Append((uint8*)TCHAR_TO_UTF8(*SourceData), GetUTF8StringSize(SourceData)); } void AGDMDebugReportRequester::AddContent(const FString& BoundaryKey, const FString& DataName, const FString& ContentType, const TArray& SourceData, TArray& OutSendData) { FString WorkStr = LineBreak + TEXT("--") + BoundaryKey + LineBreak; OutSendData.Append((uint8*)TCHAR_TO_UTF8(*WorkStr), GetUTF8StringSize(WorkStr)); WorkStr = TEXT("Content-Disposition: form-data; ") + DataName + LineBreak; OutSendData.Append((uint8*)TCHAR_TO_UTF8(*WorkStr), GetUTF8StringSize(WorkStr)); WorkStr = TEXT("Content-Type: ") + ContentType + LineBreak + LineBreak; OutSendData.Append((uint8*)TCHAR_TO_UTF8(*WorkStr), GetUTF8StringSize(WorkStr)); OutSendData.Append(SourceData); } void AGDMDebugReportRequester::AddEndContentString(const FString& BoundaryKey, TArray& OutSendData) { FString WorkStr = LineBreak + TEXT("--") + BoundaryKey + TEXT("--") + LineBreak; OutSendData.Append((uint8*)TCHAR_TO_UTF8(*WorkStr), GetUTF8StringSize(WorkStr)); } ``` `Plugin/GameDebugMenu/Source/GameDebugMenu/Private/Reports/GDMRequesterJira.cpp`: ```cpp /** * Copyright (c) 2020 akihiko moroi * * This software is released under the MIT License. * (See accompanying file LICENSE.txt or copy at http://opensource.org/licenses/MIT) */ #include "Reports/GDMRequesterJira.h" #include #include #include #include #include #include "Dom/JsonObject.h" #include "Serialization/JsonSerializer.h" #include "Serialization/JsonWriter.h" void AGDMRequesterJira::StartRequest() { bWasRequestSuccessful = false; const FGDMJiraSettings& JiraSettings = GetDefault()->JiraSettings; const FString BasicAuthData = TEXT("Basic ") + FBase64::Encode(JiraSettings.UserName + TEXT(":") + JiraSettings.AccessKey); const FString URL = TEXT("https://") + JiraSettings.HostName + TEXT("/rest/api/3/issue"); const TSharedRef Request = FHttpModule::Get().CreateRequest(); Request->OnProcessRequestComplete().BindUObject(this, &AGDMRequesterJira::OnResponseReceived); Request->SetURL(URL); Request->SetVerb(TEXT("POST")); Request->SetHeader(TEXT("Content-Type"), TEXT("application/json")); Request->SetHeader(TEXT("Accept"), TEXT("application/json")); Request->SetHeader(TEXT("Authorization"), BasicAuthData); /*こんな形式のJsonを作ってる { "fields": { "project": { "key": "AAA" }, "priority":{ "name": "BBB" }, "summary": "CCC", "issuetype": { "name": "DDD" }, "description": { "type": "doc", "version": 1, "content": [ { "type": "paragraph", "content": [ { "text": "EEE", "type": "text" } ] } ] }, } } */ TSharedRef JsonRoot = MakeShareable(new FJsonObject()); const TSharedRef JsonFields = MakeShareable(new FJsonObject()); JsonRoot->SetObjectField(TEXT("fields"), JsonFields); { const TSharedRef JsonProject = MakeShareable(new FJsonObject()); JsonFields->SetObjectField(TEXT("project"), JsonProject); { JsonProject->SetStringField(TEXT("key"), JiraSettings.ProjectKeyName); } if(JiraSettings.PriorityNameList.IsValidIndex(PriorityIndex)) { const TSharedRef JsonPriority = MakeShareable(new FJsonObject()); JsonFields->SetObjectField(TEXT("priority"), JsonPriority); { const FString PriorityStr = JiraSettings.PriorityNameList[PriorityIndex].ToString(); JsonPriority->SetStringField(TEXT("name"), PriorityStr); } } const FString AccountId = JiraSettings.GetAssigneeAccountIdByListIndex(AssigneeIndex); if(!AccountId.IsEmpty()) { const TSharedRef JsonAssignee = MakeShareable(new FJsonObject()); JsonFields->SetObjectField(TEXT("assignee"), JsonAssignee); { JsonAssignee->SetStringField(TEXT("accountId"), AccountId); } } JsonFields->SetStringField(TEXT("summary"), GetSubject()); if(JiraSettings.IssueTypeList.IsValidIndex(IssueCategoryIndex)) { const TSharedRef JsonIssueType = MakeShareable(new FJsonObject()); JsonFields->SetObjectField(TEXT("issuetype"), JsonIssueType); { const FString TypeStr = JiraSettings.IssueTypeList[IssueCategoryIndex].ToString(); JsonIssueType->SetStringField(TEXT("name"), TypeStr); } } if( JiraSettings.LabelNameList.IsValidIndex(0)) { TArray< TSharedPtr > JsonLabelArray; for( const auto& Label : JiraSettings.LabelNameList ) { JsonLabelArray.Add(MakeShareable(new FJsonValueString(Label))); } JsonFields->SetArrayField(TEXT("labels"),JsonLabelArray); } const TSharedRef JsonDescription = MakeShareable(new FJsonObject()); JsonFields->SetObjectField(TEXT("description"), JsonDescription); { JsonDescription->SetStringField(TEXT("type"), TEXT("doc")); JsonDescription->SetNumberField(TEXT("version"), 1); const TSharedRef JsonContent = MakeShareable(new FJsonObject()); TArray< TSharedPtr< FJsonValue > > Contents; Contents.Reserve(1); Contents.Emplace(MakeShareable(new FJsonValueObject(JsonContent))); JsonDescription->SetArrayField(TEXT("content"), Contents); { JsonContent->SetStringField(TEXT("type"), TEXT("paragraph")); const TSharedRef JsonContent_2 = MakeShareable(new FJsonObject()); TArray< TSharedPtr< FJsonValue > > Contents_2; Contents_2.Reserve(1); Contents_2.Emplace(MakeShareable(new FJsonValueObject(JsonContent_2))); JsonContent->SetArrayField(TEXT("content"), Contents_2); { JsonContent_2->SetStringField(TEXT("text"), GetDescription()); JsonContent_2->SetStringField(TEXT("type"), TEXT("text")); } } } } FString Content; const auto Writer = TJsonWriterFactory<>::Create(&Content); FJsonSerializer::Serialize(JsonRoot, Writer); Request->SetContentAsString(Content); Request->ProcessRequest(); } void AGDMRequesterJira::RequestUploadContent(const FString& IssueKey) { const FGDMJiraSettings& JiraSettings = GetDefault()->JiraSettings; const FString BasicAuthData = TEXT("Basic ") + FBase64::Encode(JiraSettings.UserName + TEXT(":") + JiraSettings.AccessKey); const FString URL = TEXT("https://") + JiraSettings.HostName + TEXT("/rest/api/3/issue/") + IssueKey + TEXT("/attachments"); const FString BoundaryKey = MakeBoundaryString(); const TSharedRef Request = FHttpModule::Get().CreateRequest(); Request->OnProcessRequestComplete().BindUObject(this, &AGDMRequesterJira::OnResponseReceivedUploaded); Request->SetURL(URL); Request->SetVerb(TEXT("POST")); Request->SetHeader(TEXT("X-Atlassian-Token"), TEXT("no-check")); Request->SetHeader(TEXT("Authorization"), BasicAuthData); Request->SetHeader(TEXT("Content-Type"), FString::Printf(TEXT("multipart/form-data; boundary=%s"), *BoundaryKey)); TArray ContentData; /* 画像データ */ if(bSendScreenshotCapture && (ScreenshotImageData.Num() > 0)) { const FString DataName = FString::Printf(TEXT("name=\"file\"; filename=\"%s.jpg\""), *ScreenshotCapturedDateTime.ToString()); AddContent(BoundaryKey, DataName, TEXT("image/jpeg"), ScreenshotImageData, ContentData); } /* ゲーム内ログ */ if(bSendLogs) { FString GameLog; GetOwnerDebugMenuManager()->GetOutputLogString(GameLog, LineBreak); const FString DataName = FString::Printf(TEXT("name=\"file\"; filename=\"%s.txt\""), *UKismetSystemLibrary::GetGameName()); AddContentString(BoundaryKey, DataName, TEXT("text/plain; charset=UTF-8"), GameLog, ContentData); } AddEndContentString(BoundaryKey, ContentData); Request->SetContent(ContentData); Request->ProcessRequest(); } void AGDMRequesterJira::OnResponseReceived(FHttpRequestPtr Request, FHttpResponsePtr Response, bool bWasSuccessful) { if(bWasSuccessful != false) { const FString ContentAsString = Response->GetContentAsString(); UE_LOG(LogGDM, Log, TEXT("OnResponseReceived: Success %s: ResponseCode %d"), *ContentAsString, Response->GetResponseCode()); const auto JsonReader = TJsonReaderFactory::Create(ContentAsString); TSharedPtr JsonObject = MakeShareable(new FJsonObject()); if(FJsonSerializer::Deserialize(JsonReader, JsonObject)) { const FString IssueKey = JsonObject->GetStringField(TEXT("key")); if( !IssueKey.IsEmpty() && (bSendScreenshotCapture || bSendLogs) ) { RequestUploadContent(IssueKey); } else { SuccessRequest(); } } else { UE_LOG(LogGDM, Warning, TEXT("OnResponseReceived: Deserialize error")); FailedRequest(); } } else { UE_LOG(LogGDM, Error, TEXT("OnResponseReceived: failed to connect")); FailedRequest(); } } void AGDMRequesterJira::OnResponseReceivedUploaded(FHttpRequestPtr Request, FHttpResponsePtr Response, bool bWasSuccessful) { if(bWasSuccessful) { const FString ContentAsString = Response->GetContentAsString(); UE_LOG(LogGDM, Log, TEXT("OnResponseReceivedUploaded: Success %s ResponseCode %d"), *ContentAsString, Response->GetResponseCode()); SuccessRequest(); } else { UE_LOG(LogGDM, Error, TEXT("OnResponseReceivedUploaded: failed to connect")); FailedRequest(); } } ``` `Plugin/GameDebugMenu/Source/GameDebugMenu/Private/Reports/GDMRequesterRedmine.cpp`: ```cpp /** * Copyright (c) 2020 akihiko moroi * * This software is released under the MIT License. * (See accompanying file LICENSE.txt or copy at http://opensource.org/licenses/MIT) */ #include "Reports/GDMRequesterRedmine.h" #include "HttpModule.h" #include "Serialization/JsonWriter.h" #include "Serialization/JsonSerializer.h" #include "GameDebugMenuSettings.h" #include #include "GameDebugMenuManager.h" void AGDMRequesterRedmine::StartRequest() { bWasRequestSuccessful = false; TokenScreenshotCapture.Reset(); TokenLog.Reset(); if(bSendScreenshotCapture && (ScreenshotImageData.Num() > 0)) { RequestUploadScreenshotCapture(); } else if(bSendLogs) { RequestUploadLog(); } else { RequestIssues(); } } void AGDMRequesterRedmine::RequestUploadScreenshotCapture() { FString URL(TEXT("http://")); URL += GetDefault()->RedmineSettings.HostName; URL += TEXT("/redmine/uploads.json?key="); URL += GetDefault()->RedmineSettings.AccessKey; const TSharedRef Request = FHttpModule::Get().CreateRequest(); Request->OnProcessRequestComplete().BindUObject(this, &AGDMRequesterRedmine::OnResponseReceivedUploaded); Request->SetURL(URL); Request->SetVerb(TEXT("POST")); Request->SetHeader(TEXT("Content-Type"), TEXT("application/octet-stream")); Request->SetContent(ScreenshotImageData); Request->ProcessRequest(); } void AGDMRequesterRedmine::RequestUploadLog() { /* ゲーム内ログ */ FString GameLog; GetOwnerDebugMenuManager()->GetOutputLogString(GameLog, LineBreak); FString URL(TEXT("http://")); URL += GetDefault()->RedmineSettings.HostName; URL += TEXT("/redmine/uploads.json?key="); URL += GetDefault()->RedmineSettings.AccessKey; const TSharedRef Request = FHttpModule::Get().CreateRequest(); Request->OnProcessRequestComplete().BindUObject(this, &AGDMRequesterRedmine::OnResponseReceivedLog); Request->SetURL(URL); Request->SetVerb(TEXT("POST")); Request->SetHeader(TEXT("Content-Type"), TEXT("application/octet-stream")); Request->SetContentAsString(GameLog); Request->ProcessRequest(); } void AGDMRequesterRedmine::RequestIssues() { FString URL(TEXT("http://")); URL += GetDefault()->RedmineSettings.HostName; URL += TEXT("/redmine/issues.json?key="); URL += GetDefault()->RedmineSettings.AccessKey; const TSharedRef Request = FHttpModule::Get().CreateRequest(); Request->OnProcessRequestComplete().BindUObject(this, &AGDMRequesterRedmine::OnResponseReceived); Request->SetURL(URL); Request->SetVerb(TEXT("POST")); Request->SetHeader(TEXT("Content-Type"), TEXT("application/json")); const TSharedRef JsonRootObject = MakeShareable(new FJsonObject()); const TSharedRef JsonIssueObject = MakeShareable(new FJsonObject()); JsonRootObject->SetObjectField(TEXT("issue"), JsonIssueObject); JsonIssueObject->SetStringField(TEXT("project_id"), FString::FromInt(GetDefault()->RedmineSettings.ProjectId)); JsonIssueObject->SetStringField(TEXT("tracker_id"), FString::FromInt(IssueCategoryIndex + 1)); JsonIssueObject->SetStringField(TEXT("priority_id"), FString::FromInt(PriorityIndex + 1)); JsonIssueObject->SetStringField(TEXT("subject"), GetSubject()); JsonIssueObject->SetStringField(TEXT("description"), GetDescription()); TArray< TSharedPtr< FJsonValue > > UploadObjects; const TSharedRef JsonUploadObjectCapture = MakeShareable(new FJsonObject()); const TSharedRef JsonUploadObjectLog = MakeShareable(new FJsonObject()); bool bUploads = false; if (!TokenScreenshotCapture.IsEmpty()) { bUploads = true; UploadObjects.Add(MakeShareable(new FJsonValueObject(JsonUploadObjectCapture))); JsonUploadObjectCapture->SetStringField(TEXT("token"), TokenScreenshotCapture); JsonUploadObjectCapture->SetStringField(TEXT("filename"), FString::Printf(TEXT("%s.jpg"), *ScreenshotCapturedDateTime.ToString())); JsonUploadObjectCapture->SetStringField(TEXT("content_type"), TEXT("image/jpg")); } if(!TokenLog.IsEmpty()) { bUploads = true; UploadObjects.Add(MakeShareable(new FJsonValueObject(JsonUploadObjectLog))); JsonUploadObjectLog->SetStringField(TEXT("token"), TokenLog); JsonUploadObjectLog->SetStringField(TEXT("filename"), FString::Printf(TEXT("%s.txt"), *UKismetSystemLibrary::GetGameName())); JsonUploadObjectLog->SetStringField(TEXT("content_type"), TEXT("text/plain")); } if(bUploads) { JsonIssueObject->SetArrayField("uploads", UploadObjects); } FString Content; const auto Writer = TJsonWriterFactory<>::Create(&Content); FJsonSerializer::Serialize(JsonRootObject, Writer); Request->SetContentAsString(Content); Request->ProcessRequest(); } void AGDMRequesterRedmine::OnResponseReceived(FHttpRequestPtr Request, FHttpResponsePtr Response, bool bWasSuccessful) { if (bWasSuccessful != false) { const FString ContentAsString = Response->GetContentAsString(); UE_LOG(LogGDM, Log, TEXT("OnResponseReceived: Success %s: ResponseCode %d"), *ContentAsString, Response->GetResponseCode()); SuccessRequest(); } else { UE_LOG(LogGDM, Error, TEXT("OnResponseReceived: failed to connect")); FailedRequest(); } } void AGDMRequesterRedmine::OnResponseReceivedUploaded(FHttpRequestPtr Request, FHttpResponsePtr Response, bool bWasSuccessful) { if (bWasSuccessful) { const FString ContentAsString = Response->GetContentAsString(); UE_LOG(LogGDM, Log, TEXT("OnResponseReceivedUploaded: Success %s: ResponseCode %d"), *ContentAsString, Response->GetResponseCode()); const auto JsonReader = TJsonReaderFactory::Create(ContentAsString); TSharedPtr JsonObject = MakeShareable(new FJsonObject()); if (FJsonSerializer::Deserialize(JsonReader, JsonObject)) { const auto UploadObj = JsonObject->GetObjectField(TEXT("upload")); TokenScreenshotCapture = UploadObj->GetStringField(TEXT("token")); if(bSendLogs) { RequestUploadLog(); } else { RequestIssues(); } } else { UE_LOG(LogGDM, Warning, TEXT("OnResponseReceivedUploaded: Deserialize error")); FailedRequest(); } } else { UE_LOG(LogGDM, Error, TEXT("OnResponseReceivedUploaded: failed to connect")); FailedRequest(); } } void AGDMRequesterRedmine::OnResponseReceivedLog(FHttpRequestPtr Request, FHttpResponsePtr Response, bool bWasSuccessful) { if(bWasSuccessful) { const FString ContentAsString = Response->GetContentAsString(); UE_LOG(LogGDM, Log, TEXT("OnResponseReceivedLog: Success %s: ResponseCode %d"), *ContentAsString, Response->GetResponseCode()); const auto JsonReader = TJsonReaderFactory::Create(ContentAsString); TSharedPtr JsonObject = MakeShareable(new FJsonObject()); if(FJsonSerializer::Deserialize(JsonReader, JsonObject)) { const auto UploadObj = JsonObject->GetObjectField(TEXT("upload")); TokenLog = UploadObj->GetStringField(TEXT("token")); RequestIssues(); } else { UE_LOG(LogGDM, Warning, TEXT("OnResponseReceivedLog: Deserialize error")); FailedRequest(); } } else { UE_LOG(LogGDM, Error, TEXT("OnResponseReceivedLog: failed to connect")); FailedRequest(); } } ``` `Plugin/GameDebugMenu/Source/GameDebugMenu/Private/Reports/GDMRequesterTrello.cpp`: ```cpp /** * Copyright (c) 2020 akihiko moroi * * This software is released under the MIT License. * (See accompanying file LICENSE.txt or copy at http://opensource.org/licenses/MIT) */ #include "Reports/GDMRequesterTrello.h" #include "HttpModule.h" #include "Serialization/JsonSerializer.h" #include "GameDebugMenuSettings.h" #include #include "GameDebugMenuManager.h" void AGDMRequesterTrello::StartRequest() { bWasRequestSuccessful = false; const FGDMTrelloSettings& TrelloSettings = GetDefault()->TrelloSettings; AttachmentCardListID.Reset(); FString URL(TEXT("https://api.trello.com/1/cards?")); URL += TEXT("?pos=top"); URL += TEXT("&idList="); URL += TrelloSettings.CardListIDs[IssueCategoryIndex]; URL += TEXT("&key="); URL += TrelloSettings.AccessKey; URL += TEXT("&token="); URL += TrelloSettings.AccessToken; const TSharedRef Request = FHttpModule::Get().CreateRequest(); Request->OnProcessRequestComplete().BindUObject(this, &AGDMRequesterTrello::OnResponseReceived); Request->SetURL(URL); Request->SetVerb(TEXT("POST")); const FString BoundaryKey = MakeBoundaryString(); Request->SetHeader(TEXT("Content-Type"), FString::Printf(TEXT("multipart/form-data; boundary=%s"), *BoundaryKey)); TArray ContentData; AddContentString(BoundaryKey, TEXT("name=\"name\";"), TEXT("text/plain; charset=UTF-8"), GetSubject(), ContentData); AddContentString(BoundaryKey, TEXT("name=\"desc\";"), TEXT("text/plain; charset=UTF-8"), GetDescription(), ContentData); AddEndContentString(BoundaryKey, ContentData); Request->SetContent(ContentData); Request->ProcessRequest(); } void AGDMRequesterTrello::RequestUploadScreenshotCapture() { const FGDMTrelloSettings& TrelloSettings = GetDefault()->TrelloSettings; FString URL(TEXT("https://api.trello.com/1/cards/")); URL += AttachmentCardListID; URL += TEXT("/attachments?"); URL += TEXT("&key="); URL += TrelloSettings.AccessKey; URL += TEXT("&token="); URL += TrelloSettings.AccessToken; const TSharedRef Request = FHttpModule::Get().CreateRequest(); Request->OnProcessRequestComplete().BindUObject(this, &AGDMRequesterTrello::OnResponseReceivedUploaded); Request->SetURL(URL); Request->SetVerb(TEXT("POST")); const FString BoundaryKey = MakeBoundaryString(); Request->SetHeader(TEXT("Content-Type"), FString::Printf(TEXT("multipart/form-data; boundary=%s"), *BoundaryKey)); TArray ContentData; /* 画像データ */ const FString DataName = FString::Printf(TEXT("name=\"file\"; filename=\"%s.jpg\""),*ScreenshotCapturedDateTime.ToString()); AddContent(BoundaryKey,DataName, TEXT("image/jpeg"), ScreenshotImageData, ContentData); AddEndContentString(BoundaryKey, ContentData); Request->SetContent(ContentData); Request->ProcessRequest(); } void AGDMRequesterTrello::RequestUploadLog() { const FGDMTrelloSettings& TrelloSettings = GetDefault()->TrelloSettings; FString URL(TEXT("https://api.trello.com/1/cards/")); URL += AttachmentCardListID; URL += TEXT("/attachments?"); URL += TEXT("&key="); URL += TrelloSettings.AccessKey; URL += TEXT("&token="); URL += TrelloSettings.AccessToken; const TSharedRef Request = FHttpModule::Get().CreateRequest(); Request->OnProcessRequestComplete().BindUObject(this, &AGDMRequesterTrello::OnResponseReceivedLog); Request->SetURL(URL); Request->SetVerb(TEXT("POST")); const FString BoundaryKey = MakeBoundaryString(); Request->SetHeader(TEXT("Content-Type"), FString::Printf(TEXT("multipart/form-data; boundary=%s"), *BoundaryKey)); TArray ContentData; /* ゲーム内ログ */ FString GameLog; GetOwnerDebugMenuManager()->GetOutputLogString(GameLog, LineBreak); const FString DataName = FString::Printf(TEXT("name=\"file\"; filename=\"%s.txt\""), *UKismetSystemLibrary::GetGameName()); AddContentString(BoundaryKey, DataName, TEXT("text/plain; charset=UTF-8"), GameLog, ContentData); AddEndContentString(BoundaryKey, ContentData); Request->SetContent(ContentData); Request->ProcessRequest(); } void AGDMRequesterTrello::OnResponseReceived(FHttpRequestPtr Request, FHttpResponsePtr Response, bool bWasSuccessful) { if (bWasSuccessful) { const FString ContentAsString = Response->GetContentAsString(); UE_LOG(LogGDM, Log, TEXT("OnResponseReceived Success [%s] ResponseCode[%d]"), *ContentAsString, Response->GetResponseCode()); const auto JsonReader = TJsonReaderFactory::Create(ContentAsString); TSharedPtr JsonObject = MakeShareable(new FJsonObject()); if (FJsonSerializer::Deserialize(JsonReader, JsonObject)) { AttachmentCardListID = JsonObject->GetStringField(TEXT("id")); if(!AttachmentCardListID.IsEmpty()) { if( bSendScreenshotCapture && (ScreenshotImageData.Num() > 0) ) { /* 画像データを送信 */ RequestUploadScreenshotCapture(); } else if(bSendLogs) { /* ログを送信 */ RequestUploadLog(); } else { SuccessRequest(); } } else { SuccessRequest(); } } else { UE_LOG(LogGDM, Warning, TEXT("OnResponseReceived: Deserialize error")); FailedRequest(); } } else { UE_LOG(LogGDM, Error, TEXT("OnResponseReceived: failed to connect")); FailedRequest(); } } void AGDMRequesterTrello::OnResponseReceivedUploaded(FHttpRequestPtr Request, FHttpResponsePtr Response, bool bWasSuccessful) { if (bWasSuccessful) { const FString ContentAsString = Response->GetContentAsString(); UE_LOG(LogGDM, Log, TEXT("OnResponseReceivedUploaded: Success %s: ResponseCode %d"), *ContentAsString, Response->GetResponseCode()); const auto JsonReader = TJsonReaderFactory::Create(ContentAsString); TSharedPtr JsonObject = MakeShareable(new FJsonObject()); if(FJsonSerializer::Deserialize(JsonReader, JsonObject)) { if(bSendLogs) { /* ログデータを送信 */ RequestUploadLog(); } else { SuccessRequest(); } } else { UE_LOG(LogGDM, Warning, TEXT("OnResponseReceivedUploaded: Deserialize error")); FailedRequest(); } } else { UE_LOG(LogGDM, Error, TEXT("OnResponseReceivedUploaded: failed to connect")); FailedRequest(); } } void AGDMRequesterTrello::OnResponseReceivedLog(FHttpRequestPtr Request, FHttpResponsePtr Response, bool bWasSuccessful) { if(bWasSuccessful) { const FString ContentAsString = Response->GetContentAsString(); UE_LOG(LogGDM, Log, TEXT("OnResponseReceivedLog: Success %s: ResponseCode %d"), *ContentAsString, Response->GetResponseCode()); SuccessRequest(); } else { UE_LOG(LogGDM, Error, TEXT("OnResponseReceivedLog: failed to connect")); FailedRequest(); } } ``` `Plugin/GameDebugMenu/Source/GameDebugMenu/Private/Widgets/GDMButton.cpp`: ```cpp /** * Copyright (c) 2020 akihiko moroi * * This software is released under the MIT License. * (See accompanying file LICENSE.txt or copy at http://opensource.org/licenses/MIT) */ #include "Widgets/GDMButton.h" #include "Widgets/Input/SButton.h" #include #include #include "Components/ButtonSlot.h" #define LOCTEXT_NAMESPACE "GameDebugMenu" UGDMButton::UGDMButton(const FObjectInitializer& ObjectInitializer) : Super(ObjectInitializer) { InitIsFocusable(false); /* デフォのDownAndUpだと別UserWidget上でボタンをクリックしたときに1度無視され2回クリックしないと応答しないことがあるため変更 */ SetClickMethod(EButtonClickMethod::Type::MouseDown); } #if WITH_EDITOR const FText UGDMButton::GetPaletteCategory() { return LOCTEXT("GDM", "GDM"); } #endif TSharedRef UGDMButton::RebuildWidget() { MyButton = SNew(SButton) .OnClicked(BIND_UOBJECT_DELEGATE(FOnClicked, GDMSlateHandleClicked)) .OnPressed(BIND_UOBJECT_DELEGATE(FSimpleDelegate, GDMSlateHandlePressed)) .OnReleased(BIND_UOBJECT_DELEGATE(FSimpleDelegate, GDMSlateHandleReleased)) .OnHovered_UObject(this, &ThisClass::GDMSlateHandleHovered) .OnUnhovered_UObject(this, &ThisClass::GDMSlateHandleUnhovered) .ButtonStyle(&GetStyle()) .ClickMethod(GetClickMethod()) .TouchMethod(GetTouchMethod()) .IsFocusable(GetIsFocusable()) ; if(GetChildrenCount() > 0) { Cast(GetContentSlot())->BuildSlot(MyButton.ToSharedRef()); } return MyButton.ToSharedRef(); } FReply UGDMButton::GDMSlateHandleClicked() { if(const AGameDebugMenuManager* DebugMenuManager = UGameDebugMenuFunctions::GetGameDebugMenuManager(this)) { if(DebugMenuManager->IsInputIgnored()) { return FReply::Unhandled(); } } return SlateHandleClicked(); } void UGDMButton::GDMSlateHandlePressed() { if(const AGameDebugMenuManager* DebugMenuManager = UGameDebugMenuFunctions::GetGameDebugMenuManager(this)) { if(DebugMenuManager->IsInputIgnored()) { return; } } SlateHandlePressed(); } void UGDMButton::GDMSlateHandleReleased() { if(const AGameDebugMenuManager* DebugMenuManager = UGameDebugMenuFunctions::GetGameDebugMenuManager(this)) { if(DebugMenuManager->IsInputIgnored()) { return; } } SlateHandleReleased(); } void UGDMButton::GDMSlateHandleHovered() { if(const AGameDebugMenuManager* DebugMenuManager = UGameDebugMenuFunctions::GetGameDebugMenuManager(this)) { if(DebugMenuManager->IsInputIgnored()) { return; } } SlateHandleHovered(); } void UGDMButton::GDMSlateHandleUnhovered() { if(const AGameDebugMenuManager* DebugMenuManager = UGameDebugMenuFunctions::GetGameDebugMenuManager(this)) { if(DebugMenuManager->IsInputIgnored()) { return; } } SlateHandleUnhovered(); } #undef LOCTEXT_NAMESPACE ``` `Plugin/GameDebugMenu/Source/GameDebugMenu/Private/Widgets/GDMComboBoxString.cpp`: ```cpp /** * Copyright (c) 2020 akihiko moroi * * This software is released under the MIT License. * (See accompanying file LICENSE.txt or copy at http://opensource.org/licenses/MIT) */ #include "Widgets/GDMComboBoxString.h" #include #define LOCTEXT_NAMESPACE "GDMComboBoxString" UGDMComboBoxString::UGDMComboBoxString(const FObjectInitializer& ObjectInitializer) : Super(ObjectInitializer) { InitIsFocusable(false); if(!IsRunningDedicatedServer()) { InitFont(FSlateFontInfo(GetDefault()->GetDebugMenuFont(), 14, FName("Bold"))); } } #if WITH_EDITOR const FText UGDMComboBoxString::GetPaletteCategory() { return LOCTEXT("GDM", "GDM"); } #endif #undef LOCTEXT_NAMESPACE ``` `Plugin/GameDebugMenu/Source/GameDebugMenu/Private/Widgets/GDMDebugReportWidget.cpp`: ```cpp /** * Copyright (c) 2020 akihiko moroi * * This software is released under the MIT License. * (See accompanying file LICENSE.txt or copy at http://opensource.org/licenses/MIT) */ #include "Widgets/GDMDebugReportWidget.h" #include "GameDebugMenuSettings.h" #include "Reports/GDMRequesterRedmine.h" #include "Reports/GDMRequesterTrello.h" #include "GameDebugMenuManager.h" #include "GameDebugMenuFunctions.h" #include "ImageUtils.h" #include "Engine/Texture2D.h" UGDMDebugReportWidget::UGDMDebugReportWidget(const FObjectInitializer& ObjectInitializer) : Super(ObjectInitializer) , OnReceivedReportDispatcher() , ScreenshotTexture(nullptr) , ScreenshotImageData() , TestCount(0) , MaxTestCount(0) , ScreenshotCapturedDateTime() , CurrentDebugReportRequester(nullptr) { } void UGDMDebugReportWidget::SendDebugReport(const FString& Subject, const FString& Description, int32 IssueCategoryIndex, int32 PriorityIndex, int32 AssigneeIndex,bool bSendLogs, bool bSendScreenshotCapture) { AGameDebugMenuManager* DebugMenuManager = UGameDebugMenuFunctions::GetGameDebugMenuManager(this); check(DebugMenuManager != nullptr); const TSubclassOf* RequesterClass = GetDefault()->GetDebugReportRequesterClass(); if( RequesterClass == nullptr ) { OnReceivedReportDispatcher.Broadcast(false); return; } CurrentDebugReportRequester = GetWorld()->SpawnActor(*RequesterClass); CurrentDebugReportRequester->SetOwner(DebugMenuManager); if (CurrentDebugReportRequester.IsValid()) { CurrentDebugReportRequester->bSendLogs = bSendLogs; CurrentDebugReportRequester->bSendScreenshotCapture = bSendScreenshotCapture; CurrentDebugReportRequester->Subject = Subject; CurrentDebugReportRequester->Description = Description; CurrentDebugReportRequester->IssueCategoryIndex = IssueCategoryIndex; CurrentDebugReportRequester->PriorityIndex = PriorityIndex; CurrentDebugReportRequester->AssigneeIndex = AssigneeIndex; CurrentDebugReportRequester->ScreenshotImageData = ScreenshotImageData; CurrentDebugReportRequester->ScreenshotCapturedDateTime = ScreenshotCapturedDateTime; CurrentDebugReportRequester->TestCount = TestCount; CurrentDebugReportRequester->MaxTestCount = MaxTestCount; CurrentDebugReportRequester->OnDestroyed.AddDynamic(this, &UGDMDebugReportWidget::OnRequesterDestroyed); CurrentDebugReportRequester->StartRequest(); } } TArray UGDMDebugReportWidget::GetIssueCategoryNameList() { return GetDefault()->GetIssueCategoryNameList(); } TArray UGDMDebugReportWidget::GetPriorityNameList() { return GetDefault()->GetPriorityNameList(); } TArray UGDMDebugReportWidget::GetAssigneeNameList() { return GetDefault()->GetAssigneeNameList(); } int32 UGDMDebugReportWidget::GetDefaultIssueCategoryIndex() { return GetDefault()->GetDefaultIssueCategoryIndex(); } int32 UGDMDebugReportWidget::GetDefaultPriorityIndex() { return GetDefault()->GetDefaultPriorityIndex(); } void UGDMDebugReportWidget::OnScreenshotCaptured(int32 Width, int32 Height, const TArray& Bitmap) { /* Debugレポートようにキャッシュ */ ScreenshotImageData.Reset(); const TArray BitmapCopy(Bitmap); FImageUtils::ThumbnailCompressImageArray(Width, Height, BitmapCopy, ScreenshotImageData); UTexture2D* NewTexture = FImageUtils::ImportBufferAsTexture2D(ScreenshotImageData); NewTexture->SRGB = true; // /* テクスチャを生成 */ // UTexture2D* NewTexture = UTexture2D::CreateTransient(Width, Height, EPixelFormat::PF_B8G8R8A8); // NewTexture->SRGB = true; // // /* テクスチャをコピーする */ // void* TextureData = NewTexture->GetPlatformData()->Mips[0].BulkData.Lock(LOCK_READ_WRITE); // const int32 TextureDataSize = Bitmap.Num() * 4; // FMemory::Memcpy(TextureData, Bitmap.GetData(), TextureDataSize); // NewTexture->GetPlatformData()->Mips[0].BulkData.Unlock(); // // /* 更新 */ // NewTexture->UpdateResource(); ScreenshotTexture = NewTexture; ScreenshotCapturedDateTime = FDateTime::Now(); OnCreatedScreenshotTexture(ScreenshotTexture); } void UGDMDebugReportWidget::OnRequesterDestroyed(AActor* DestroyedActor) { AGDMDebugReportRequester* Requester = Cast(DestroyedActor); if (IsValid(Requester)) { OnReceivedReportDispatcher.Broadcast(Requester->bWasRequestSuccessful); } else { OnReceivedReportDispatcher.Broadcast(false); } CurrentDebugReportRequester = nullptr; } bool UGDMDebugReportWidget::IsRequesting() { return CurrentDebugReportRequester.IsValid(); } ``` `Plugin/GameDebugMenu/Source/GameDebugMenu/Private/Widgets/GDMFunctionWidget.cpp`: ```cpp /** * Copyright (c) 2020 akihiko moroi * * This software is released under the MIT License. * (See accompanying file LICENSE.txt or copy at http://opensource.org/licenses/MIT) */ #include "Widgets/GDMFunctionWidget.h" #include "GameDebugMenuFunctions.h" bool UGDMFunctionWidget::TryCallObjectFunction(FName EventName) { return GDMProcessEvent(EventName, nullptr); } bool UGDMFunctionWidget::GDMProcessEvent(FName EventName, void* Parms) { if(!IsValid(TargetObject)) { UE_LOG(LogGDM, Warning, TEXT("GDMFunctionWidget ProcessEvent: Not found Object")); return false; } if(EventName == NAME_None) { UE_LOG(LogGDM, Warning, TEXT("GDMFunctionWidget ProcessEvent: Not found EventName")); return false; } UFunction* TargetFunction = TargetObject->FindFunction(EventName); if(!IsValid(TargetFunction)) { UE_LOG(LogGDM, Warning, TEXT("GDMFunctionWidget ProcessEvent: Not found Function")); return false; } if(TargetFunction->NumParms != 0) { UE_LOG(LogGDM, Warning, TEXT("GDMFunctionWidget ProcessEvent: Arguments are not supported: %d"), TargetFunction->NumParms); return false; } TargetObject->ProcessEvent(TargetFunction, nullptr); UGameDebugMenuFunctions::GetGameDebugMenuManager(this)->CallExecuteProcessEventDispatcher(EventName, TargetObject); return true; } ``` `Plugin/GameDebugMenu/Source/GameDebugMenu/Private/Widgets/GDMIntSpinBox.cpp`: ```cpp /** * Copyright (c) 2020 akihiko moroi * * This software is released under the MIT License. * (See accompanying file LICENSE.txt or copy at http://opensource.org/licenses/MIT) */ #include "Widgets/GDMIntSpinBox.h" #include "UObject/ConstructorHelpers.h" #include "Engine/Font.h" #include #define LOCTEXT_NAMESPACE "UMG" UGDMIntSpinBox::UGDMIntSpinBox(const FObjectInitializer& ObjectInitializer) : Super(ObjectInitializer) { if (!IsRunningDedicatedServer()) { Font = FSlateFontInfo(GetDefault()->GetDebugMenuFont(), 12, FName("Bold")); } // Grab other defaults from slate arguments. SSpinBox::FArguments Defaults; Value = Defaults._Value.Get(); MinValue = Defaults._MinValue.Get().Get(0); MaxValue = Defaults._MaxValue.Get().Get(0); MinSliderValue = Defaults._MinSliderValue.Get().Get(0); MaxSliderValue = Defaults._MaxSliderValue.Get().Get(0); Delta = Defaults._Delta.Get(); SliderExponent = Defaults._SliderExponent.Get(); MinDesiredWidth = Defaults._MinDesiredWidth.Get(); ClearKeyboardFocusOnCommit = Defaults._ClearKeyboardFocusOnCommit.Get(); SelectAllTextOnCommit = Defaults._SelectAllTextOnCommit.Get(); WidgetStyle = *Defaults._Style; ForegroundColor = FSlateColor(FLinearColor::Black); } void UGDMIntSpinBox::ReleaseSlateResources(bool bReleaseChildren) { Super::ReleaseSlateResources(bReleaseChildren); MySpinBox.Reset(); } TSharedRef UGDMIntSpinBox::RebuildWidget() { MySpinBox = SNew(SSpinBox) .Style(&WidgetStyle) .Font(Font) .ClearKeyboardFocusOnCommit(ClearKeyboardFocusOnCommit) .SelectAllTextOnCommit(SelectAllTextOnCommit) .Justification(Justification) .OnValueChanged(BIND_UOBJECT_DELEGATE(FOnInt32ValueChanged, HandleOnValueChanged)) .OnValueCommitted(BIND_UOBJECT_DELEGATE(FOnInt32ValueCommitted, HandleOnValueCommitted)) .OnBeginSliderMovement(BIND_UOBJECT_DELEGATE(FSimpleDelegate, HandleOnBeginSliderMovement)) .OnEndSliderMovement(BIND_UOBJECT_DELEGATE(FOnInt32ValueChanged, HandleOnEndSliderMovement)) ; return MySpinBox.ToSharedRef(); } void UGDMIntSpinBox::SynchronizeProperties() { Super::SynchronizeProperties(); MySpinBox->SetDelta(Delta); MySpinBox->SetSliderExponent(SliderExponent); MySpinBox->SetMinDesiredWidth(MinDesiredWidth); MySpinBox->SetForegroundColor(ForegroundColor); // Set optional values bOverride_MinValue ? SetMinValue(MinValue) : ClearMinValue(); bOverride_MaxValue ? SetMaxValue(MaxValue) : ClearMaxValue(); bOverride_MinSliderValue ? SetMinSliderValue(MinSliderValue) : ClearMinSliderValue(); bOverride_MaxSliderValue ? SetMaxSliderValue(MaxSliderValue) : ClearMaxSliderValue(); // Always set the value last so that the max/min values are taken into account. TAttribute ValueBinding = PROPERTY_BINDING(int32, Value); MySpinBox->SetValue(ValueBinding); } int32 UGDMIntSpinBox::GetValue() const { if (MySpinBox.IsValid()) { return MySpinBox->GetValue(); } return Value; } void UGDMIntSpinBox::SetValue(int32 InValue) { Value = InValue; if (MySpinBox.IsValid()) { MySpinBox->SetValue(InValue); } } // MIN VALUE int32 UGDMIntSpinBox::GetMinValue() const { int32 ReturnVal = TNumericLimits::Lowest(); if (MySpinBox.IsValid()) { ReturnVal = MySpinBox->GetMinValue(); } else if (bOverride_MinValue) { ReturnVal = MinValue; } return ReturnVal; } void UGDMIntSpinBox::SetMinValue(int32 InMinValue) { bOverride_MinValue = true; MinValue = InMinValue; if (MySpinBox.IsValid()) { MySpinBox->SetMinValue(InMinValue); } } void UGDMIntSpinBox::ClearMinValue() { bOverride_MinValue = false; if (MySpinBox.IsValid()) { MySpinBox->SetMinValue(TOptional()); } } // MAX VALUE int32 UGDMIntSpinBox::GetMaxValue() const { int32 ReturnVal = TNumericLimits::Max(); if (MySpinBox.IsValid()) { ReturnVal = MySpinBox->GetMaxValue(); } else if (bOverride_MaxValue) { ReturnVal = MaxValue; } return ReturnVal; } void UGDMIntSpinBox::SetMaxValue(int32 InMaxValue) { bOverride_MaxValue = true; MaxValue = InMaxValue; if (MySpinBox.IsValid()) { MySpinBox->SetMaxValue(InMaxValue); } } void UGDMIntSpinBox::ClearMaxValue() { bOverride_MaxValue = false; if (MySpinBox.IsValid()) { MySpinBox->SetMaxValue(TOptional()); } } // MIN SLIDER VALUE int32 UGDMIntSpinBox::GetMinSliderValue() const { int32 ReturnVal = TNumericLimits::Min(); if (MySpinBox.IsValid()) { ReturnVal = MySpinBox->GetMinSliderValue(); } else if (bOverride_MinSliderValue) { ReturnVal = MinSliderValue; } return ReturnVal; } void UGDMIntSpinBox::SetMinSliderValue(int32 InMinSliderValue) { bOverride_MinSliderValue = true; MinSliderValue = InMinSliderValue; if (MySpinBox.IsValid()) { MySpinBox->SetMinSliderValue(InMinSliderValue); } } void UGDMIntSpinBox::ClearMinSliderValue() { bOverride_MinSliderValue = false; if (MySpinBox.IsValid()) { MySpinBox->SetMinSliderValue(TOptional()); } } // MAX SLIDER VALUE int32 UGDMIntSpinBox::GetMaxSliderValue() const { int32 ReturnVal = TNumericLimits::Max(); if (MySpinBox.IsValid()) { ReturnVal = MySpinBox->GetMaxSliderValue(); } else if (bOverride_MaxSliderValue) { ReturnVal = MaxSliderValue; } return ReturnVal; } void UGDMIntSpinBox::SetMaxSliderValue(int32 InMaxSliderValue) { bOverride_MaxSliderValue = true; MaxSliderValue = InMaxSliderValue; if (MySpinBox.IsValid()) { MySpinBox->SetMaxSliderValue(InMaxSliderValue); } } void UGDMIntSpinBox::ClearMaxSliderValue() { bOverride_MaxSliderValue = false; if (MySpinBox.IsValid()) { MySpinBox->SetMaxSliderValue(TOptional()); } } void UGDMIntSpinBox::SetForegroundColor(FSlateColor InForegroundColor) { ForegroundColor = InForegroundColor; if ( MySpinBox.IsValid() ) { MySpinBox->SetForegroundColor(ForegroundColor); } } // Event handlers void UGDMIntSpinBox::HandleOnValueChanged(int32 InValue) { if ( !IsDesignTime() ) { OnValueChanged.Broadcast(InValue); } } void UGDMIntSpinBox::HandleOnValueCommitted(int32 InValue, ETextCommit::Type CommitMethod) { if ( !IsDesignTime() ) { OnValueCommitted.Broadcast(InValue, CommitMethod); } } void UGDMIntSpinBox::HandleOnBeginSliderMovement() { if ( !IsDesignTime() ) { OnBeginSliderMovement.Broadcast(); } } void UGDMIntSpinBox::HandleOnEndSliderMovement(int32 InValue) { if ( !IsDesignTime() ) { OnEndSliderMovement.Broadcast(InValue); } } void UGDMIntSpinBox::PostLoad() { Super::PostLoad(); if ( GetLinkerUEVersion() < VER_UE4_DEPRECATE_UMG_STYLE_ASSETS ) { if ( Style_DEPRECATED != nullptr ) { const FSpinBoxStyle* StylePtr = Style_DEPRECATED->GetStyle(); if ( StylePtr != nullptr ) { WidgetStyle = *StylePtr; } Style_DEPRECATED = nullptr; } } } #if WITH_EDITOR const FText UGDMIntSpinBox::GetPaletteCategory() { return LOCTEXT("GDM", "GDM"); } #endif ///////////////////////////////////////////////////// #undef LOCTEXT_NAMESPACE ``` `Plugin/GameDebugMenu/Source/GameDebugMenu/Private/Widgets/GDMPropertyWidget.cpp`: ```cpp /** * Copyright (c) 2020 akihiko moroi * * This software is released under the MIT License. * (See accompanying file LICENSE.txt or copy at http://opensource.org/licenses/MIT) */ #include "Widgets/GDMPropertyWidget.h" #include "GameDebugMenuFunctions.h" #include "GameDebugMenuTypes.h" void UGDMPropertyWidget::NativeConstruct() { Super::NativeConstruct(); } void UGDMPropertyWidget::NativeDestruct() { Super::NativeDestruct(); } void UGDMPropertyWidget::NativeTick(const FGeometry& MyGeometry, float InDeltaTime) { Super::NativeTick(MyGeometry, InDeltaTime); /* 無操作状態からリセットまでの猶予(秒) */ constexpr float InactiveResetThreshold = 0.18f; if (bStartChangeAmount) { /* 変化量の時間計測 */ if (!bChangedMaxChangeAmount) { ElapsedTime -= InDeltaTime; if (ElapsedTime <= 0.0f) { ElapsedTime = 0.0f; ChangeAmount = PropertyConfigInfo.MaxChangeAmount; bChangedMaxChangeAmount = true; } } InactiveElapsedTime += InDeltaTime; if (InactiveElapsedTime >= InactiveResetThreshold) { /* 一定時間StartChangeAmountTimeが呼ばれなくなったのでもう数値が変動してないと判断し戻す */ ResetChangeAmountTime(); } } } void UGDMPropertyWidget::StartChangeAmountTime() { if (!bStartChangeAmount) { bStartChangeAmount = true; bChangedMaxChangeAmount = false; ChangeAmount = PropertyConfigInfo.DefaultChangeAmount; ElapsedTime = PropertyConfigInfo.MaxChangeAmountTime; } /* 呼ばれるたびに無操作時間をリセット */ InactiveElapsedTime = 0.0f; } void UGDMPropertyWidget::ResetChangeAmountTime() { if (!bStartChangeAmount) { return; } bStartChangeAmount = false; bChangedMaxChangeAmount = false; ChangeAmount = PropertyConfigInfo.DefaultChangeAmount; InactiveElapsedTime = 0.0f; } bool UGDMPropertyWidget::GetPropertyValue_Bool(bool& bHasProperty) { bHasProperty = false; if(!IsValid(TargetObject)) { return false; } const FBoolProperty* BoolProp = CastField(TargetObject->GetClass()->FindPropertyByName(PropertyName)); if(BoolProp == nullptr) { return false; } bHasProperty = true; return BoolProp->GetPropertyValue(BoolProp->ContainerPtrToValuePtr(TargetObject)); } void UGDMPropertyWidget::SetPropertyValue_Bool(bool bNewValue, bool& bHasProperty) { bHasProperty = false; if(!IsValid(TargetObject)) { return; } const FBoolProperty* BoolProp = CastField(TargetObject->GetClass()->FindPropertyByName(PropertyName)); if(BoolProp == nullptr) { return; } bHasProperty = true; const bool bOldValue = BoolProp->GetPropertyValue(BoolProp->ContainerPtrToValuePtr(TargetObject)); if (bNewValue != bOldValue) { BoolProp->SetPropertyValue(BoolProp->ContainerPtrToValuePtr(TargetObject), bNewValue); UGameDebugMenuFunctions::GetGameDebugMenuManager(this)->CallChangePropertyBoolDispatcher(PropertyName, TargetObject, bNewValue, bOldValue, PropertySaveKey); } } float UGDMPropertyWidget::GetPropertyValue_Float(bool& bHasProperty) { bHasProperty = false; if(!IsValid(TargetObject)) { return 0.0f; } const FFloatProperty* FloatProp = CastField(TargetObject->GetClass()->FindPropertyByName(PropertyName)); if(FloatProp == nullptr) { if(const FDoubleProperty* DoubleProp = CastField(TargetObject->GetClass()->FindPropertyByName(PropertyName))) { bHasProperty = true; return DoubleProp->GetPropertyValue(DoubleProp->ContainerPtrToValuePtr(TargetObject)); } return 0.0f; } bHasProperty = true; return FloatProp->GetPropertyValue(FloatProp->ContainerPtrToValuePtr(TargetObject)); } void UGDMPropertyWidget::SetPropertyValue_Float(float NewValue, bool& bHasProperty) { bHasProperty = false; if(!IsValid(TargetObject)) { return; } const FFloatProperty* FloatProp = CastField(TargetObject->GetClass()->FindPropertyByName(PropertyName)); if(FloatProp == nullptr) { if(const FDoubleProperty* DoubleProp = CastField(TargetObject->GetClass()->FindPropertyByName(PropertyName))) { bHasProperty = true; const float OldValue = DoubleProp->GetPropertyValue(DoubleProp->ContainerPtrToValuePtr(TargetObject)); if (FMath::IsNearlyEqual(NewValue, OldValue) == false) { DoubleProp->SetPropertyValue(DoubleProp->ContainerPtrToValuePtr(TargetObject), NewValue); UGameDebugMenuFunctions::GetGameDebugMenuManager(this)->CallChangePropertyFloatDispatcher(PropertyName, TargetObject, NewValue, OldValue, PropertySaveKey); } } return; } bHasProperty = true; const float OldValue = FloatProp->GetPropertyValue(FloatProp->ContainerPtrToValuePtr(TargetObject)); if (FMath::IsNearlyEqual(NewValue, OldValue) == false) { FloatProp->SetPropertyValue(FloatProp->ContainerPtrToValuePtr(TargetObject), NewValue); UGameDebugMenuFunctions::GetGameDebugMenuManager(this)->CallChangePropertyFloatDispatcher(PropertyName, TargetObject, NewValue, OldValue, PropertySaveKey); } } int32 UGDMPropertyWidget::GetPropertyValue_Int(bool& bHasProperty) { bHasProperty = false; if(!IsValid(TargetObject)) { return 0; } const FIntProperty* IntProp = CastField(TargetObject->GetClass()->FindPropertyByName(PropertyName)); if(IntProp == nullptr) { return 0; } bHasProperty = true; return IntProp->GetPropertyValue(IntProp->ContainerPtrToValuePtr(TargetObject)); } void UGDMPropertyWidget::SetPropertyValue_Int(int32 NewValue, bool& bHasProperty) { bHasProperty = false; if(!IsValid(TargetObject)) { return; } const FIntProperty* IntProp = CastField(TargetObject->GetClass()->FindPropertyByName(PropertyName)); if(IntProp == nullptr) { return; } bHasProperty = true; const int32 OldValue = IntProp->GetPropertyValue(IntProp->ContainerPtrToValuePtr(TargetObject)); if (NewValue != OldValue) { IntProp->SetPropertyValue(IntProp->ContainerPtrToValuePtr(TargetObject), NewValue); UGameDebugMenuFunctions::GetGameDebugMenuManager(this)->CallChangePropertyIntDispatcher(PropertyName, TargetObject, NewValue, OldValue, PropertySaveKey); } } uint8 UGDMPropertyWidget::GetPropertyValue_Byte(bool& bHasProperty) { bHasProperty = false; if(!IsValid(TargetObject)) { return 0; } const FProperty* Prop = TargetObject->GetClass()->FindPropertyByName(PropertyName); if( Prop == nullptr ) { return 0; } const FEnumProperty* EnumProp = CastField(Prop); if( EnumProp != nullptr ) { bHasProperty = true; const FNumericProperty* NumProp = EnumProp->GetUnderlyingProperty(); const uint64 Value = NumProp->GetUnsignedIntPropertyValue(EnumProp->ContainerPtrToValuePtr(TargetObject)); return static_cast( Value ); } const FByteProperty* ByteProp = CastField(Prop); if( ByteProp != nullptr ) { bHasProperty = true; return ByteProp->GetPropertyValue(ByteProp->ContainerPtrToValuePtr(TargetObject)); } return 0; } void UGDMPropertyWidget::SetPropertyValue_Byte(uint8 NewValue, bool& bHasProperty) { bHasProperty = false; if(!IsValid(TargetObject)) { return; } const FProperty* Prop = TargetObject->GetClass()->FindPropertyByName(PropertyName); if( Prop == nullptr ) { return; } const FEnumProperty* EnumProp = CastField(Prop); if( EnumProp != nullptr ) { bHasProperty = true; const FNumericProperty* NumProp = EnumProp->GetUnderlyingProperty(); const uint8 OldValue = static_cast( NumProp->GetUnsignedIntPropertyValue(EnumProp->ContainerPtrToValuePtr(TargetObject)) ); if( NewValue != OldValue ) { NumProp->SetIntPropertyValue(EnumProp->ContainerPtrToValuePtr(TargetObject),static_cast( NewValue )); UGameDebugMenuFunctions::GetGameDebugMenuManager(this)->CallChangePropertyByteDispatcher(PropertyName,TargetObject,NewValue,OldValue, PropertySaveKey); } return; } const FByteProperty* ByteProp = CastField(Prop); if( ByteProp != nullptr ) { bHasProperty = true; const uint8 OldValue = ByteProp->GetPropertyValue(ByteProp->ContainerPtrToValuePtr(TargetObject)); if( NewValue != OldValue ) { ByteProp->SetPropertyValue(ByteProp->ContainerPtrToValuePtr(TargetObject),NewValue); UGameDebugMenuFunctions::GetGameDebugMenuManager(this)->CallChangePropertyByteDispatcher(PropertyName, TargetObject, NewValue, OldValue, PropertySaveKey); } } } TArray UGDMPropertyWidget::GetEnumDisplayNames(const FString& EnumPath, bool& bHasProperty) { TArray Result; const UEnum* Enum = FindObject(nullptr, *EnumPath); if(!IsValid(Enum)) { return Result; } const int32 Num = Enum->NumEnums() - 1; for(int32 Index = 0;Index < Num; ++Index) { Result.Add(Enum->GetDisplayNameTextByIndex(Index)); } bHasProperty = true; return Result; } FString UGDMPropertyWidget::GetPropertyValue_String(bool& bHasProperty) { bHasProperty = false; if(!IsValid(TargetObject)) { return FString(); } const FStrProperty* StrProp = CastField(TargetObject->GetClass()->FindPropertyByName(PropertyName)); if (StrProp == nullptr) { return FString(); } bHasProperty = true; return StrProp->GetPropertyValue(StrProp->ContainerPtrToValuePtr(TargetObject)); } void UGDMPropertyWidget::SetPropertyValue_String(FString NewValue, bool& bHasProperty) { bHasProperty = false; if(!IsValid(TargetObject)) { return; } const FStrProperty* StrProp = CastField(TargetObject->GetClass()->FindPropertyByName(PropertyName)); if (StrProp == nullptr) { return; } bHasProperty = true; const FString OldValue = StrProp->GetPropertyValue(StrProp->ContainerPtrToValuePtr(TargetObject)); if (NewValue != OldValue) { StrProp->SetPropertyValue(StrProp->ContainerPtrToValuePtr(TargetObject), NewValue); UGameDebugMenuFunctions::GetGameDebugMenuManager(this)->CallChangePropertyStringDispatcher(PropertyName, TargetObject, NewValue, OldValue, PropertySaveKey); } } FVector UGDMPropertyWidget::GetPropertyValue_Vector(bool& bHasProperty) { bHasProperty = false; if( !IsValid(TargetObject) ) { return FVector::ZeroVector; } const FStructProperty* StructProp = CastField(TargetObject->GetClass()->FindPropertyByName(PropertyName)); if( StructProp == nullptr ) { return FVector::ZeroVector; } const UStruct* Struct = StructProp->Struct; if( Struct == nullptr ) { return FVector::ZeroVector; } if( Struct->IsChildOf(TBaseStructure::Get()) ) { if(const FVector* Value = StructProp->ContainerPtrToValuePtr(TargetObject) ) { bHasProperty = true; return (*Value); } } return FVector::ZeroVector; } void UGDMPropertyWidget::SetPropertyValue_Vector(FVector NewValue, bool& bHasProperty) { bHasProperty = false; if( !IsValid(TargetObject) ) { return; } const FStructProperty* StructProp = CastField(TargetObject->GetClass()->FindPropertyByName(PropertyName)); if( StructProp == nullptr ) { return; } const UStruct* Struct = StructProp->Struct; if( Struct == nullptr ) { return; } if( Struct->IsChildOf(TBaseStructure::Get()) ) { if( FVector* Value = StructProp->ContainerPtrToValuePtr(TargetObject) ) { const FVector OldValue = *Value; bHasProperty = true; if( NewValue != OldValue ) { *Value = NewValue; UGameDebugMenuFunctions::GetGameDebugMenuManager(this)->CallChangePropertyVectorDispatcher(PropertyName, TargetObject, NewValue, OldValue, PropertySaveKey); } } } } FVector2D UGDMPropertyWidget::GetPropertyValue_Vector2D(bool& bHasProperty) { bHasProperty = false; if( !IsValid(TargetObject) ) { return FVector2D::ZeroVector; } const FStructProperty* StructProp = CastField(TargetObject->GetClass()->FindPropertyByName(PropertyName)); if( StructProp == nullptr ) { return FVector2D::ZeroVector; } const UStruct* Struct = StructProp->Struct; if( Struct == nullptr ) { return FVector2D::ZeroVector; } if( Struct->IsChildOf(TBaseStructure::Get()) ) { if(const FVector2D* Value = StructProp->ContainerPtrToValuePtr(TargetObject) ) { bHasProperty = true; return (*Value); } } return FVector2D::ZeroVector; } void UGDMPropertyWidget::SetPropertyValue_Vector2D(FVector2D NewValue, bool& bHasProperty) { bHasProperty = false; if( !IsValid(TargetObject) ) { return; } const FStructProperty* StructProp = CastField(TargetObject->GetClass()->FindPropertyByName(PropertyName)); if( StructProp == nullptr ) { return; } const UStruct* Struct = StructProp->Struct; if( Struct == nullptr ) { return; } if( Struct->IsChildOf(TBaseStructure::Get()) ) { if( FVector2D* Value = StructProp->ContainerPtrToValuePtr(TargetObject) ) { const FVector2D OldValue = *Value; bHasProperty = true; if( NewValue != OldValue ) { *Value = NewValue; UGameDebugMenuFunctions::GetGameDebugMenuManager(this)->CallChangePropertyVector2DDispatcher(PropertyName, TargetObject, NewValue, OldValue, PropertySaveKey); } } } } FRotator UGDMPropertyWidget::GetPropertyValue_Rotator(bool& bHasProperty) { bHasProperty = false; if( !IsValid(TargetObject) ) { return FRotator::ZeroRotator; } const FStructProperty* StructProp = CastField(TargetObject->GetClass()->FindPropertyByName(PropertyName)); if( StructProp == nullptr ) { return FRotator::ZeroRotator; } const UStruct* Struct = StructProp->Struct; if( Struct == nullptr ) { return FRotator::ZeroRotator; } if( Struct->IsChildOf(TBaseStructure::Get()) ) { if(const FRotator* Value = StructProp->ContainerPtrToValuePtr(TargetObject) ) { bHasProperty = true; return (*Value); } } return FRotator::ZeroRotator; } void UGDMPropertyWidget::SetPropertyValue_Rotator(FRotator NewValue, bool& bHasProperty) { bHasProperty = false; if( !IsValid(TargetObject) ) { return; } const FStructProperty* StructProp = CastField(TargetObject->GetClass()->FindPropertyByName(PropertyName)); if( StructProp == nullptr ) { return; } const UStruct* Struct = StructProp->Struct; if( Struct == nullptr ) { return; } if( Struct->IsChildOf(TBaseStructure::Get()) ) { if( FRotator* Value = StructProp->ContainerPtrToValuePtr(TargetObject) ) { const FRotator OldValue = *Value; bHasProperty = true; if( NewValue != OldValue ) { *Value = NewValue; UGameDebugMenuFunctions::GetGameDebugMenuManager(this)->CallChangePropertyRotatorDispatcher(PropertyName, TargetObject, NewValue, OldValue, PropertySaveKey); } } } } ``` `Plugin/GameDebugMenu/Source/GameDebugMenu/Private/Widgets/GDMTextBlock.cpp`: ```cpp /** * Copyright (c) 2020 akihiko moroi * * This software is released under the MIT License. * (See accompanying file LICENSE.txt or copy at http://opensource.org/licenses/MIT) */ #include "Widgets/GDMTextBlock.h" #include "GameDebugMenuFunctions.h" #include "GameDebugMenuSettings.h" #include "GameDebugMenuManager.h" #include "Component/GDMLocalizeStringComponent.h" #include "Engine/Engine.h" #include "Widgets/GameDebugMenuRootWidget.h" #include "Widgets/Text/STextBlock.h" #define LOCTEXT_NAMESPACE "GameDebugMenu" UGDMTextBlock::UGDMTextBlock(const FObjectInitializer& ObjectInitializer) : Super(ObjectInitializer) , DebugMenuStringKey() #if WITH_EDITORONLY_DATA , PreviewLanguageKey(TEXT("Japanese")) #endif { if( !IsRunningDedicatedServer() ) { SetFont(FSlateFontInfo(GetDefault()->GetDebugMenuFont(), 24, FName("Bold"))); } } void UGDMTextBlock::SynchronizeProperties() { Super::SynchronizeProperties(); if( !DebugMenuStringKey.IsEmpty() ) { SetText(FText::FromString(DebugMenuStringKey)); } } void UGDMTextBlock::SetText(FText InText) { FString StringKey = InText.ToString(); if( DebugMenuStringKey != StringKey ) { DebugMenuStringKey.Reset(); } UWorld* World = GetWorld(); if( !IsValid(World) ) { if( UGameDebugMenuRootWidget* RootWidget = Cast(GetOuter()) ) { World = RootWidget->GetOwnerManager()->GetWorld(); } else { World = GEngine->GetWorldFromContextObject(World, EGetWorldErrorMode::LogAndReturnNull); } } FString DebugMenuStr; #if WITH_EDITORONLY_DATA if( !InText.IsEmpty() ) { if( IsValid(World) ) { /* テキストのStringKeyなら取得しセットするテキストを上書きする */ if( World->WorldType == EWorldType::Editor || World->WorldType == EWorldType::EditorPreview ) { DebugMenuStr = GetDefault()->GetDebugMenuString(PreviewLanguageKey, StringKey); if( !DebugMenuStr.IsEmpty() ) { DebugMenuStringKey = InText.ToString(); InText = FText::FromString(DebugMenuStr); } } else if( AGameDebugMenuManager* Manager = UGameDebugMenuFunctions::GetGameDebugMenuManager(World) ) { if( Manager->GetLocalizeStringComponent()->GetString(StringKey, DebugMenuStr) ) { DebugMenuStringKey = InText.ToString(); InText = FText::FromString(DebugMenuStr); } } } } #else if( !InText.IsEmpty() ) { if( UGameDebugMenuFunctions::GetDebugMenuString(World, StringKey, DebugMenuStr) ) { DebugMenuStringKey = InText.ToString(); InText = FText::FromString(DebugMenuStr); } } #endif Super::SetText(InText); } void UGDMTextBlock::SetWrapTextAt(float InWrapTextAt) { WrapTextAt = InWrapTextAt; if( MyTextBlock.IsValid() ) { MyTextBlock->SetWrapTextAt(InWrapTextAt); } } #if WITH_EDITOR const FText UGDMTextBlock::GetPaletteCategory() { return LOCTEXT("GDM", "GDM"); } bool UGDMTextBlock::CanEditChange(const FProperty* InProperty) const { return Super::CanEditChange(InProperty); } #endif #undef LOCTEXT_NAMESPACE ``` `Plugin/GameDebugMenu/Source/GameDebugMenu/Private/Widgets/GameDebugMenuRootWidget.cpp`: ```cpp /** * Copyright (c) 2020 akihiko moroi * * This software is released under the MIT License. * (See accompanying file LICENSE.txt or copy at http://opensource.org/licenses/MIT) */ #include "Widgets/GameDebugMenuRootWidget.h" #include "GameDebugMenuManager.h" #include "Input/GDMInputSystemComponent.h" void UGameDebugMenuRootWidget::NativeConstruct() { Super::NativeConstruct(); /* ルートは常にアクティブ状態に */ bActivateMenu = true; } void UGameDebugMenuRootWidget::NativeDestruct() { Super::NativeDestruct(); } void UGameDebugMenuRootWidget::ActivateDebugMenu() { /* ルートは使用しない */ } void UGameDebugMenuRootWidget::DeactivateDebugMenu() { /* ルートは使用しない */ } void UGameDebugMenuRootWidget::SetDebugMenuManager(AGameDebugMenuManager* InManager) { Manager = InManager; } AGameDebugMenuManager* UGameDebugMenuRootWidget::GetOwnerManager() const { return Manager; } void UGameDebugMenuRootWidget::SwitchInputComponentGroupForGameDebugMenu(const FName NewGroupName) { Manager->GetDebugMenuInputSystemComponent()->SwitchToInputGroup(NewGroupName); } ``` `Plugin/GameDebugMenu/Source/GameDebugMenu/Private/Widgets/GameDebugMenuWidget.cpp`: ```cpp /** * Copyright (c) 2020 akihiko moroi * * This software is released under the MIT License. * (See accompanying file LICENSE.txt or copy at http://opensource.org/licenses/MIT) */ #include "Widgets/GameDebugMenuWidget.h" #include #include "Engine/DebugCameraController.h" #include "GameDebugMenuManager.h" #include "Component/GDMPlayerControllerProxyComponent.h" #include "GameDebugMenuFunctions.h" #include "GameDebugMenuSettings.h" #include "Input/GDMEnhancedInputComponent.h" UGameDebugMenuWidget::UGameDebugMenuWidget(const FObjectInitializer& ObjectInitializer) : Super(ObjectInitializer) , OnSendWidgetEventDispatcher() , bActivateMenu(false) , InputHandles() { /* UE5.7+: UUserWidgetはBPの入力ノード有無に応じてInputComponentを自動生成/登録し得る。 * GameDebugMenuはUGDMInputSystemComponentでPush/Popを自前管理するため、自動登録は無効化しておく*/ #if ENGINE_MAJOR_VERSION > 5 || (ENGINE_MAJOR_VERSION == 5 && ENGINE_MINOR_VERSION >= 7) bAutomaticallyRegisterInputOnConstruction = false; #endif } void UGameDebugMenuWidget::CreateInputComponent() { /* 手動で生成するので何もしない */ } void UGameDebugMenuWidget::StartProcessingInputScriptDelegates() { /* 何もしない(意図的) * GameDebugMenuはUGDMInputSystemComponentで入力スタックを自前管理する。 * ここでSuperを呼ぶとUUserWidgetがInputComponentを勝手にPushして、bBlockInput等でゲーム入力を塞ぎ得る。*/ } void UGameDebugMenuWidget::StopProcessingInputScriptDelegates() { /* 何もしない(意図的) * StartProcessingInputScriptDelegates() と対で、UUserWidgetの自動Pop/解除経路が * プラグイン管理の入力スタックと干渉しないようにする。*/ } void UGameDebugMenuWidget::EnsureDebugMenuInputComponent() { if (IsValid(InputComponent)) { return; } if ( APlayerController* Controller = GetOwningPlayer() ) { const UClass* Class = GetDefault()->GetDebugMenuInputComponentClass(); InputComponent = NewObject( Controller, Class, NAME_None, RF_Transient); } } APlayerController* UGameDebugMenuWidget::GetOriginalPlayerController() const { APlayerController* PlayerController = GetOwningPlayer(); const ADebugCameraController* DCC = Cast(PlayerController); if (IsValid(DCC)) { /* FLocalPlayerContextのGetPlayerControllerはADebugCameraControllerを無視できないのでオリジナルを返す */ return DCC->OriginalControllerRef; } return PlayerController; } UGDMEnhancedInputComponent* UGameDebugMenuWidget::GetMyInputComponent() const { return Cast(InputComponent); } bool UGameDebugMenuWidget::RegisterDebugMenuWidgetInputFunction(const UInputAction* Action, const FName FunctionName, const ETriggerEvent TriggerEvent, UObject* FunctionObject) { EnsureDebugMenuInputComponent(); if (UGDMEnhancedInputComponent* InputComp = GetMyInputComponent()) { if (!IsValid(FunctionObject)) { FunctionObject = this; } InputHandles.Add(InputComp->BindDebugMenuAction(Action, TriggerEvent, FunctionObject, FunctionName).GetHandle()); return true; } return false; } bool UGameDebugMenuWidget::RegisterDebugMenuWidgetInputEvent(const UInputAction* Action, FOnGameDebugMenuWidgetInputAction Callback, const ETriggerEvent TriggerEvent) { EnsureDebugMenuInputComponent(); if (UGDMEnhancedInputComponent* InputComp = GetMyInputComponent()) { const FEnhancedInputActionEventBinding& Binding = InputComp->BindDebugMenuAction( Action, TriggerEvent, [WeakThis = TWeakObjectPtr(this), Callback](const FInputActionInstance& Instance) { if (WeakThis.IsValid() && Callback.IsBound()) { Callback.Execute(); } }); InputHandles.Add(Binding.GetHandle()); return true; } return false; } void UGameDebugMenuWidget::UnregisterDebugMenuWidgetInputs() { if (UGDMEnhancedInputComponent* InputComp = GetMyInputComponent()) { for (const auto Handle : InputHandles) { InputComp->RemoveBindingByHandle(Handle); } InputHandles.Reset(); } } void UGameDebugMenuWidget::SendSelfEvent(FName EventName) { OnSendWidgetEventDispatcher.Broadcast(this, EventName); } void UGameDebugMenuWidget::ExecuteGDMConsoleCommand(const FString Command, const EGDMConsoleCommandNetType CommandNetType) { if (const APlayerController* PC = GetOriginalPlayerController() ) { if( UGDMPlayerControllerProxyComponent* DebugMenuPCProxyComponent = PC->FindComponentByClass() ) { DebugMenuPCProxyComponent->ExecuteConsoleCommand(Command, CommandNetType); } } } bool UGameDebugMenuWidget::IsActivateDebugMenu() { return bActivateMenu; } void UGameDebugMenuWidget::ActivateDebugMenu() { if (bActivateMenu) { return; } bActivateMenu = true; UGameDebugMenuFunctions::RegisterInputComponentForGameDebugMenu(this, InputComponent); OnActivateDebugMenu(); } void UGameDebugMenuWidget::DeactivateDebugMenu() { if (!bActivateMenu) { return; } bActivateMenu = false; UGameDebugMenuFunctions::UnregisterInputComponentForGameDebugMenu(this, InputComponent); OnDeactivateDebugMenu(); } void UGameDebugMenuWidget::OnChangeDebugMenuLanguage(const FName& NewLanguageKey, const FName& OldLanguageKey) { OnChangeDebugMenuLanguageBP(NewLanguageKey, OldLanguageKey); } bool UGameDebugMenuWidget::GetWidgetChildrenOfClass(TSubclassOf WidgetClass, TArray& OutChildWidgets, bool bEndSearchAsYouFind) { OutChildWidgets.Reset(); /* 現在チェックするWidget郡 */ TInlineComponentArray WidgetsToCheck; /* チェック済みWidget郡 */ TInlineComponentArray CheckedWidgets; /* 作業用 */ TArray WorkWidgets; WidgetsToCheck.Push(this); while( WidgetsToCheck.Num() > 0 ) { UWidget* PossibleParent = WidgetsToCheck.Pop(EAllowShrinking::No); if( CheckedWidgets.Contains(PossibleParent) ) { /* チェック済み */ continue; } CheckedWidgets.Add(PossibleParent); WorkWidgets.Reset(); if(const UUserWidget* UserWidget = Cast(PossibleParent) ) { if( UserWidget->WidgetTree != nullptr ) { UserWidget->WidgetTree->GetAllWidgets(WorkWidgets); } } else { UWidgetTree::GetChildWidgets(PossibleParent, WorkWidgets); } for( UWidget* Widget : WorkWidgets ) { if( CheckedWidgets.Contains(Widget) ) { /* チェック済み */ continue; } if( Widget->GetClass()->IsChildOf(WidgetClass) ) { OutChildWidgets.Add(Widget); if( bEndSearchAsYouFind ) { /* 一致したものがあればそのまま終了 */ return (OutChildWidgets.Num() > 0); } } WidgetsToCheck.Push(Widget); } } return (OutChildWidgets.Num() > 0); } ``` `Plugin/GameDebugMenu/Source/GameDebugMenu/Public/Component/GDMListenerComponent.h`: ```h /** * Copyright (c) 2020 akihiko moroi * * This software is released under the MIT License. * (See accompanying file LICENSE.txt or copy at http://opensource.org/licenses/MIT) */ #pragma once #include "CoreMinimal.h" #include "GDMListenerComponent.generated.h" DECLARE_DYNAMIC_MULTICAST_DELEGATE(FGDMGameDebugMenuListenerDelegate); DECLARE_DYNAMIC_MULTICAST_DELEGATE_OneParam(FGDMOnExecuteConsoleCommandDelegate, const FString&, Command); DECLARE_DYNAMIC_MULTICAST_DELEGATE_TwoParams(FGDMOnExecuteProcessEventDelegate, const FName&, FunctionName, UObject*, FunctionOwnerObject); DECLARE_DYNAMIC_MULTICAST_DELEGATE_FiveParams(FGDMOnChangePropertyBoolDelegate, const FName&, PropertyName, UObject*, PropertyOwnerObject, bool, New, bool, Old, const FString&, PropertySaveKey); DECLARE_DYNAMIC_MULTICAST_DELEGATE_FiveParams(FGDMOnChangePropertyIntDelegate, const FName&, PropertyName, UObject*, PropertyOwnerObject, int32, New, int32, Old, const FString&, PropertySaveKey); DECLARE_DYNAMIC_MULTICAST_DELEGATE_FiveParams(FGDMOnChangePropertyFloatDelegate, const FName&, PropertyName, UObject*, PropertyOwnerObject, float, New, float, Old, const FString&, PropertySaveKey); DECLARE_DYNAMIC_MULTICAST_DELEGATE_FiveParams(FGDMOnChangePropertyByteDelegate, const FName&, PropertyName, UObject*, PropertyOwnerObject, uint8, New, uint8, Old, const FString&, PropertySaveKey); DECLARE_DYNAMIC_MULTICAST_DELEGATE_FiveParams(FGDMOnChangePropertyStringDelegate, const FName&, PropertyName, UObject*, PropertyOwnerObject, FString, New, FString, Old, const FString&, PropertySaveKey); DECLARE_DYNAMIC_MULTICAST_DELEGATE_FiveParams(FGDMOnChangePropertyVectorDelegate, const FName&, PropertyName, UObject*, PropertyOwnerObject, FVector, New, FVector, Old, const FString&, PropertySaveKey); DECLARE_DYNAMIC_MULTICAST_DELEGATE_FiveParams(FGDMOnChangePropertyVector2DDelegate, const FName&, PropertyName, UObject*, PropertyOwnerObject, FVector2D, New, FVector2D, Old, const FString&, PropertySaveKey); DECLARE_DYNAMIC_MULTICAST_DELEGATE_FiveParams(FGDMOnChangePropertyRotatorDelegate, const FName&, PropertyName, UObject*, PropertyOwnerObject, FRotator, New, FRotator, Old, const FString&, PropertySaveKey); DECLARE_DYNAMIC_MULTICAST_DELEGATE_OneParam(FGDMOnInputSystemDelegate, UObject*, TargetInputObject); DECLARE_DYNAMIC_MULTICAST_DELEGATE_TwoParams(FGDMOnInputSystemChangeInputObjectDelegate, UObject*, NewInputObject, UObject*, OldInputObject); DECLARE_DYNAMIC_MULTICAST_DELEGATE_TwoParams(FGDMOnChangeDebugMenuLanguageDelegate, const FName&, NewLanguageKey, const FName&, OldLanguageKey); /** * DebugMenuでのイベントを取得できるコンポーネント */ UCLASS(Blueprintable, ClassGroup = (GameDebugMenu), hidecategories = Object, meta = (BlueprintSpawnableComponent)) class GAMEDEBUGMENU_API UGDMListenerComponent : public UActorComponent { GENERATED_BODY() public: /** DebugMenuが表示されたとき呼ばれるイベント */ UPROPERTY(BlueprintAssignable, Category = "GDM|Dispatcher") FGDMGameDebugMenuListenerDelegate OnShowDispatcher; /** DebugMenuが閉じたとき呼ばれるイベント */ UPROPERTY(BlueprintAssignable, Category = "GDM|Dispatcher") FGDMGameDebugMenuListenerDelegate OnHideDispatcher; /** DebugMenuからコンソールコマンド実行した時に呼ばれるイベント */ UPROPERTY(BlueprintAssignable, Category = "GDM|Dispatcher") FGDMOnExecuteConsoleCommandDelegate OnExecuteConsoleCommandDispatcher; /** DebugMenuに登録された関数が実行されたとき呼ばれるイベント */ UPROPERTY(BlueprintAssignable, Category = "GDM|Dispatcher") FGDMOnExecuteProcessEventDelegate OnExecuteProcessEventDispatcher; /** DebugMenuに登録されたプロパティ(Bool)が変更されたとき呼ばれるイベント */ UPROPERTY(BlueprintAssignable, Category = "GDM|Dispatcher") FGDMOnChangePropertyBoolDelegate OnChangePropertyBoolDispatcher; /** DebugMenuに登録されたプロパティ(Int)が変更されたとき呼ばれるイベント */ UPROPERTY(BlueprintAssignable, Category = "GDM|Dispatcher") FGDMOnChangePropertyIntDelegate OnChangePropertyIntDispatcher; /** DebugMenuに登録されたプロパティ(Float)が変更されたとき呼ばれるイベント */ UPROPERTY(BlueprintAssignable, Category = "GDM|Dispatcher") FGDMOnChangePropertyFloatDelegate OnChangePropertyFloatDispatcher; /** DebugMenuに登録されたプロパティ(Byte)が変更されたとき呼ばれるイベント */ UPROPERTY(BlueprintAssignable, Category = "GDM|Dispatcher") FGDMOnChangePropertyByteDelegate OnChangePropertyByteDispatcher; /** DebugMenuに登録されたプロパティ(String)が変更されたとき呼ばれるイベント */ UPROPERTY(BlueprintAssignable, Category = "GDM|Dispatcher") FGDMOnChangePropertyStringDelegate OnChangePropertyStringDispatcher; /** DebugMenuに登録されたプロパティ(Vector)が変更されたとき呼ばれるイベント */ UPROPERTY(BlueprintAssignable, Category = "GDM|Dispatcher") FGDMOnChangePropertyVectorDelegate OnChangePropertyVectorDispatcher; /** DebugMenuに登録されたプロパティ(Vector2D)が変更されたとき呼ばれるイベント */ UPROPERTY(BlueprintAssignable, Category = "GDM|Dispatcher") FGDMOnChangePropertyVector2DDelegate OnChangePropertyVector2DDispatcher; /** DebugMenuに登録されたプロパティ(Rotator)が変更されたとき呼ばれるイベント */ UPROPERTY(BlueprintAssignable, Category = "GDM|Dispatcher") FGDMOnChangePropertyRotatorDelegate OnChangePropertyRotatorDispatcher; /** DebugMenuの使用言語が変更されたときに呼ばれるイベント */ UPROPERTY(BlueprintAssignable, Category = "GDM|Dispatcher") FGDMOnChangeDebugMenuLanguageDelegate OnChangeDebugMenuLanguageDispatcher; /** DebugMenuでスクショ処理の開始時に呼ばれるイベント */ UPROPERTY(BlueprintAssignable, Category = "GDM|Dispatcher") FGDMGameDebugMenuListenerDelegate OnStartScreenshotRequestDispatcher; /** DebugMenuでスクショ処理の終了時に呼ばれるイベント */ UPROPERTY(BlueprintAssignable, Category = "GDM|Dispatcher") FGDMGameDebugMenuListenerDelegate OnScreenshotRequestProcessedDispatcher; /** DebugMenuでロードが完了した時に呼ばれるイベント */ UPROPERTY(BlueprintAssignable, Category = "GDM|Dispatcher") FGDMGameDebugMenuListenerDelegate OnLoadedDebugMenuDispatcher; /** DebugMenuでセーブが完了した時に呼ばれるイベント */ UPROPERTY(BlueprintAssignable, Category = "GDM|Dispatcher") FGDMGameDebugMenuListenerDelegate OnSavedDebugMenuDispatcher; /** DebugMenuのセーブが削除した時に呼ばれるイベント */ UPROPERTY(BlueprintAssignable, Category = "GDM|Dispatcher") FGDMGameDebugMenuListenerDelegate OnDeletedDebugMenuDispatcher; public: UGDMListenerComponent(const FObjectInitializer& ObjectInitializer = FObjectInitializer::Get()); virtual void InitializeComponent() override; virtual void UninitializeComponent() override; public: UFUNCTION(BlueprintCallable) void AllUnbindDispatchers(); public: static int32 PushListenerComponent(UWorld* TargetWorld, UGDMListenerComponent* Listener); static int32 PopListenerComponent(UWorld* TargetWorld, UGDMListenerComponent* Listener); static void GetAllListenerComponents(UWorld* TargetWorld, TArray& OutListenerComponents); }; ``` `Plugin/GameDebugMenu/Source/GameDebugMenu/Public/Component/GDMLocalizeStringComponent.h`: ```h /** * Copyright (c) 2020 akihiko moroi * * This software is released under the MIT License. * (See accompanying file LICENSE.txt or copy at http://opensource.org/licenses/MIT) */ #pragma once #include "CoreMinimal.h" #include "GameDebugMenuTypes.h" #include "Components/ActorComponent.h" #include "GDMLocalizeStringComponent.generated.h" class UGDMPropertyJsonSystemComponent; /** * DebugMenuの文字列管理 */ UCLASS(NotBlueprintable, NotBlueprintType) class GAMEDEBUGMENU_API UGDMLocalizeStringComponent : public UActorComponent { GENERATED_BODY() /** 読み込んだ文字のキーと現在の言語にあった文字列 */ UPROPERTY(Transient) TMap CachedDebugMenuStrings; /** True: デバックメニュー用の StringKey を指定してる箇所をそのまま表示する */ UPROPERTY(Transient) bool bCurrentDebugMenuDirectStringKey; /** 現在のデバックメニューの言語 */ UPROPERTY(Transient) FName CurrentLanguage; public: UGDMLocalizeStringComponent(); public: /** * Json情報を取得しセットする */ void SetJsonSystemComponentValue(UGDMPropertyJsonSystemComponent* PropertyJsonSystemComponent); /** * Json情報を取得しセットする */ void SetToJsonSystemComponent(UGDMPropertyJsonSystemComponent* PropertyJsonSystemComponent, const FString& Language); /** * DebugMenu用のローカライズされた文字列を返す */ bool GetString(const FString& StringKey, FString& OutString); void SyncLoadDebugMenuStringTables(); const FName& GetCurrentDebugMenuLanguage() const; }; /************************************************************************************************************************************/ inline bool UGDMLocalizeStringComponent::GetString(const FString& StringKey, FString& OutString) { if( FString* SourceString = CachedDebugMenuStrings.Find(StringKey) ) { if( bCurrentDebugMenuDirectStringKey ) { /* 取得できたキーをそのまま戻す */ OutString = StringKey; } else { OutString = *SourceString; } } else { OutString = StringKey; UE_LOG(LogGDM, Verbose, TEXT("GetDebugMenuString: Not found StringKey %s"), *StringKey); return false; } return true; } inline const FName& UGDMLocalizeStringComponent::GetCurrentDebugMenuLanguage() const { return CurrentLanguage; } ``` `Plugin/GameDebugMenu/Source/GameDebugMenu/Public/Component/GDMPlayerControllerProxyComponent.h`: ```h /** * Copyright (c) 2020 akihiko moroi * * This software is released under the MIT License. * (See accompanying file LICENSE.txt or copy at http://opensource.org/licenses/MIT) */ #pragma once #include "CoreMinimal.h" #include "GameDebugMenuTypes.h" #include "GDMPlayerControllerProxyComponent.generated.h" class AGameDebugMenuManager; /** * DebugMenuManager生成時、PlayerControllerに自動で追加されるコンポーネント * 使用用途としてはマルチプレイなどでDebug目的の通信が必要な場合、PlayerControllerの代わりに記述などができる */ UCLASS(Blueprintable, NotBlueprintType, noteditinlinenew) class GAMEDEBUGMENU_API UGDMPlayerControllerProxyComponent : public UActorComponent { GENERATED_BODY() public: UPROPERTY(Replicated) TObjectPtr DebugMenuManager = nullptr; public: UGDMPlayerControllerProxyComponent(); virtual void GetLifetimeReplicatedProps(TArray& OutLifetimeProps ) const override; virtual void BeginPlay() override; virtual void EndPlay(const EEndPlayReason::Type EndPlayReason) override; public: UFUNCTION(BlueprintPure) APlayerController* GetOwnerPlayerController() const; UFUNCTION(BlueprintPure) APawn* GetOwnerPlayerPawn() const; UFUNCTION(BlueprintPure) ACharacter* GetOwnerPlayerCharacter() const; UFUNCTION(BlueprintPure) AGameDebugMenuManager* GetDebugMenuManager() const; UFUNCTION(Server, Reliable, WithValidation) void ROS_ExecuteConsoleCommand(const FString& Command,bool bAllClient); UFUNCTION(Client, Reliable, WithValidation) void ROC_ExecuteConsoleCommand(const FString& Command); virtual void ExecuteConsoleCommand(const FString& Command, EGDMConsoleCommandNetType CommandNetType); virtual void AllExecuteConsoleCommand_Server(const FString& Command); }; ``` `Plugin/GameDebugMenu/Source/GameDebugMenu/Public/Component/GDMPropertyJsonSystemComponent.h`: ```h /** * Copyright (c) 2020 akihiko moroi * * This software is released under the MIT License. * (See accompanying file LICENSE.txt or copy at http://opensource.org/licenses/MIT) */ #pragma once #include "CoreMinimal.h" #include "GameDebugMenuTypes.h" #include "Components/ActorComponent.h" #include "GDMPropertyJsonSystemComponent.generated.h" /** * DebugMenu全体で管理するJsonへの読み書きを管理するコンポーネント */ UCLASS(NotBlueprintable, NotBlueprintType) class GAMEDEBUGMENU_API UGDMPropertyJsonSystemComponent : public UActorComponent { GENERATED_BODY() private: static const FString JsonField_RootProperty; static const FString JsonField_RootFunction; static const FString JsonField_RootCustom; static const FString JsonField_RootFavorite; static const FString JsonField_FavoriteDefinitionName; static const FString JsonField_FavoriteSaveKey; TSharedPtr RootJsonObject; public: UGDMPropertyJsonSystemComponent(const FObjectInitializer& ObjectInitializer = FObjectInitializer::Get()); virtual void BeginPlay() override; public: /** * 対象のオブジェクトのプロパティをJsonに追加する */ UFUNCTION(BlueprintCallable) void AddPropertyToJson(const FString& ObjectKey, UObject* TargetObject, const FString& PropertyName) const; /** * Jsonから指定プロパティを削除する */ UFUNCTION(BlueprintCallable) void RemovePropertyFromJson(const FString& ObjectKey, const FString& PropertyName) const; /** * Json内のプロパティ情報を対象オブジェクトに反映する * @return true: 反映に成功 false: データがなかったか取得に失敗した */ bool ApplyJsonToObjectProperty(const FString& ObjectKey, UObject* TargetObject, const FString& PropertyName) const; UFUNCTION(BlueprintCallable) void AddFunctionToJson(const FString& ObjectKey, UObject* TargetObject, const FString& FunctionName) const; UFUNCTION(BlueprintCallable) void RemoveFunctionFromJson(const FString& ObjectKey, const FString& FunctionName) const; bool HaveFunctionInJson(const FString& ObjectKey, UObject* TargetObject, const FString& FunctionName) const; /** * お気に入り情報を追加 */ UFUNCTION(BlueprintCallable) void AddFavoriteEntry(const FString& DefinitionName, const FString& FavoriteSaveKey); /** * お気に入り情報を削除 */ UFUNCTION(BlueprintCallable) bool RemoveFavoriteEntry(const FString& DefinitionName, const FString& FavoriteSaveKey); /** * お気に入り情報が存在するか? */ UFUNCTION(BlueprintCallable) bool HasFavoriteEntry(const FString& DefinitionName, const FString& FavoriteSaveKey) const; /** * すべてのお気に入り情報を取得する */ UFUNCTION(BlueprintCallable) TArray GetAllFavoriteEntries() const; /** * 配列の文字列をJsonにセットする */ UFUNCTION(BlueprintCallable) void SetCustomStringArray(const FString& Key, const TArray& StringArray); /** * 単一の文字列をJsonにセットする */ UFUNCTION(BlueprintCallable) void SetCustomString(const FString& Key, const FString& StringValue); /** * 配列の文字列をJsonから取得する * @param Key */ UFUNCTION(BlueprintCallable) TArray GetCustomStringArray(const FString& Key) const; /** * 単一の文字列をJsonから取得 */ UFUNCTION(BlueprintCallable) FString GetCustomString(const FString& Key, const FString& DefaultValue = TEXT("")) const; /** * Json内に指定されたキーで文字列が存在するかを確認する */ UFUNCTION(BlueprintCallable) bool HasCustomString(const FString& Key) const; /** * Jsonを文字列で取得 */ UFUNCTION(BlueprintCallable,BlueprintPure=false) FString GetJsonAsString() const; /** * 文字列からJsonを構築する */ bool BuildJsonFromString(const FString& JsonString); private: UFUNCTION() void OnChangePropertyBool(const FName& PropertyName, UObject* PropertyOwnerObject, bool New, bool Old, const FString& PropertySaveKey); UFUNCTION() void OnChangePropertyInt(const FName& PropertyName, UObject* PropertyOwnerObject, int32 New, int32 Old, const FString& PropertySaveKey); UFUNCTION() void OnChangePropertyFloat(const FName& PropertyName, UObject* PropertyOwnerObject, float New, float Old, const FString& PropertySaveKey); UFUNCTION() void OnChangePropertyByte(const FName& PropertyName, UObject* PropertyOwnerObject, uint8 New, uint8 Old, const FString& PropertySaveKey); UFUNCTION() void OnChangePropertyString(const FName& PropertyName, UObject* PropertyOwnerObject, FString New, FString Old, const FString& PropertySaveKey); UFUNCTION() void OnChangePropertyVector(const FName& PropertyName, UObject* PropertyOwnerObject, FVector New, FVector Old, const FString& PropertySaveKey); UFUNCTION() void OnChangePropertyVector2D(const FName& PropertyName, UObject* PropertyOwnerObject, FVector2D New, FVector2D Old, const FString& PropertySaveKey); UFUNCTION() void OnChangePropertyRotator(const FName& PropertyName, UObject* PropertyOwnerObject, FRotator New, FRotator Old, const FString& PropertySaveKey); }; ``` `Plugin/GameDebugMenu/Source/GameDebugMenu/Public/Component/GDMSaveSystemComponent.h`: ```h /** * Copyright (c) 2020 akihiko moroi * * This software is released under the MIT License. * (See accompanying file LICENSE.txt or copy at http://opensource.org/licenses/MIT) */ #pragma once #include "CoreMinimal.h" #include "GDMPropertyJsonSystemComponent.h" #include "Components/ActorComponent.h" #include "GameFramework/SaveGame.h" #include "GDMSaveSystemComponent.generated.h" class UGDMSaveGame; /** * DebugMenuのセーブ/ロード機能を扱うコンポーネント */ UCLASS(NotBlueprintable, NotBlueprintType) class GAMEDEBUGMENU_API UGDMSaveSystemComponent : public UActorComponent { GENERATED_BODY() UPROPERTY() int32 UserIndex; UPROPERTY() TObjectPtr SaveGame; public: UGDMSaveSystemComponent(const FObjectInitializer& ObjectInitializer = FObjectInitializer::Get()); public: /** * DebugMenuの状態を保存する */ UFUNCTION(BlueprintCallable) virtual void SaveDebugMenuFile(); /** * DebugMenuの状態を読み込む */ UFUNCTION(BlueprintCallable) virtual void LoadDebugMenuFile(); /** * DebugMenuのセーブデータを削除する */ UFUNCTION(BlueprintCallable) virtual void DeleteDebugMenuFile(); protected: UGDMPropertyJsonSystemComponent* GetPropertyJsonSystemComponent() const; virtual bool SaveFile(const FString& ContentString); virtual bool LoadFile(FString& OutLoadedContentString); virtual bool DeleteFile(); bool CanUseSaveGame(); }; UCLASS() class UGDMSaveGame : public USaveGame { GENERATED_BODY() public: UPROPERTY() FString Json; }; ``` `Plugin/GameDebugMenu/Source/GameDebugMenu/Public/Component/GDMScreenshotRequesterComponent.h`: ```h /** * Copyright (c) 2020 akihiko moroi * * This software is released under the MIT License. * (See accompanying file LICENSE.txt or copy at http://opensource.org/licenses/MIT) */ #pragma once #include "CoreMinimal.h" #include "GDMScreenshotRequesterComponent.generated.h" class AGameDebugMenuManager; DECLARE_DYNAMIC_MULTICAST_DELEGATE(FGDMGameDebugMenuScreenshotDelegate); /** * DebugMenuでのスクショ処理 */ UCLASS(NotBlueprintable, NotBlueprintType) class GAMEDEBUGMENU_API UGDMScreenshotRequesterComponent : public UActorComponent { GENERATED_BODY() protected: FDelegateHandle OnScreenshotCapturedHandle; FDelegateHandle OnScreenshotRequestProcessedHandle; bool bRequestProcessed; public: UGDMScreenshotRequesterComponent(const FObjectInitializer& ObjectInitializer = FObjectInitializer::Get()); virtual void BeginPlay() override; virtual void EndPlay(const EEndPlayReason::Type EndPlayReason) override; public: virtual void RequestScreenshot(); virtual bool IsRequestProcessed(); protected: AGameDebugMenuManager* GetOwnerGameDebugMenuManager() const; virtual void OnScreenshotCaptured(int32 Width, int32 Height, const TArray& Bitmap); virtual void OnScreenshotRequestProcessed(); virtual void ResetHandle(); }; ``` `Plugin/GameDebugMenu/Source/GameDebugMenu/Public/ConsoleCommand/GDMConsoleCommandValueProvider.h`: ```h /** * Copyright (c) 2020 akihiko moroi * * This software is released under the MIT License. * (See accompanying file LICENSE.txt or copy at http://opensource.org/licenses/MIT) */ #pragma once #include "CoreMinimal.h" #include "UObject/Object.h" #include "GDMConsoleCommandValueProvider.generated.h" /** * コンソールコマンド名に対応する値を取得するためのベースクラス。 * 複数のコンソールコマンドへのアクセス方式(例: ConsoleVariable, WorldTime等)を抽象化する。 */ UCLASS(Abstract, Blueprintable, EditInlineNew, DefaultToInstanced) class GAMEDEBUGMENU_API UGDMConsoleCommandValueProvider : public UObject { GENERATED_BODY() public: UFUNCTION(BlueprintNativeEvent, BlueprintCallable) bool GetFloatValue(const FString& CommandName, float& OutValue) const; virtual bool GetFloatValue_Implementation(const FString& CommandName, float& OutValue) const { return false; } }; ``` `Plugin/GameDebugMenu/Source/GameDebugMenu/Public/ConsoleCommand/GDMConsoleCommandValueProviderComponent.h`: ```h /** * Copyright (c) 2020 akihiko moroi * * This software is released under the MIT License. * (See accompanying file LICENSE.txt or copy at http://opensource.org/licenses/MIT) */ #pragma once #include "CoreMinimal.h" #include "Components/ActorComponent.h" #include "GDMConsoleCommandValueProvider.h" #include "GDMConsoleCommandValueProviderComponent.generated.h" class UGDMConsoleVariableCommandValueProvider; /** * 特定の部分文字列(パターン)に一致するコンソールコマンドに対し、 * 対応する ValueProvider を指定する構造体。 */ USTRUCT(BlueprintType) struct FGDMConsoleCommandProviderPattern { GENERATED_BODY() UPROPERTY(EditAnywhere, BlueprintReadWrite) FString Pattern; UPROPERTY(EditAnywhere, Instanced, BlueprintReadWrite) TObjectPtr Provider = nullptr; }; /** * 登録されたパターンに応じて適切な UGDMConsoleCommandValueProvider を選択し、 * コンソールコマンドから値を取得する ActorComponent。 * * パターンマッチが成功しない場合は ConsoleVariableCommandValueProvider を fallback として利用する。 */ UCLASS() class GAMEDEBUGMENU_API UGDMConsoleCommandValueProviderComponent : public UActorComponent { GENERATED_BODY() protected: UPROPERTY(EditAnywhere, Category="GDM") TArray ProviderPatterns; UPROPERTY(EditAnywhere, Instanced, Category="GDM") TObjectPtr ConsoleVariableCommandValueProvider; public: UGDMConsoleCommandValueProviderComponent(); UFUNCTION(BlueprintCallable, Category = "Console") bool GetFloatValue(const FString& CommandName, float& OutValue); }; ``` `Plugin/GameDebugMenu/Source/GameDebugMenu/Public/ConsoleCommand/GDMConsoleVariableCommandValueProvider.h`: ```h /** * Copyright (c) 2020 akihiko moroi * * This software is released under the MIT License. * (See accompanying file LICENSE.txt or copy at http://opensource.org/licenses/MIT) */ #pragma once #include "CoreMinimal.h" #include "GDMConsoleCommandValueProvider.h" #include "GDMConsoleVariableCommandValueProvider.generated.h" /** * ConsoleManager用プロバイダー */ UCLASS() class GAMEDEBUGMENU_API UGDMConsoleVariableCommandValueProvider : public UGDMConsoleCommandValueProvider { GENERATED_BODY() public: virtual bool GetFloatValue_Implementation(const FString& CommandName, float& OutValue) const override; }; ``` `Plugin/GameDebugMenu/Source/GameDebugMenu/Public/ConsoleCommand/GDMSlomoCommandValueProvider.h`: ```h /** * Copyright (c) 2020 akihiko moroi * * This software is released under the MIT License. * (See accompanying file LICENSE.txt or copy at http://opensource.org/licenses/MIT) */ #pragma once #include "CoreMinimal.h" #include "GDMConsoleCommandValueProvider.h" #include "GDMSlomoCommandValueProvider.generated.h" /** * Slomo用プロバイダー */ UCLASS() class GAMEDEBUGMENU_API UGDMSlomoCommandValueProvider : public UGDMConsoleCommandValueProvider { GENERATED_BODY() public: virtual bool GetFloatValue_Implementation(const FString& CommandName, float& OutValue) const override; }; ``` `Plugin/GameDebugMenu/Source/GameDebugMenu/Public/Data/GDMConsoleCommandSetAsset.h`: ```h /** * Copyright (c) 2020 akihiko moroi * * This software is released under the MIT License. * (See accompanying file LICENSE.txt or copy at http://opensource.org/licenses/MIT) */ #pragma once #include "CoreMinimal.h" #include "GameDebugMenuTypes.h" #include "Engine/DataAsset.h" #include "GDMConsoleCommandSetAsset.generated.h" /** * デバッグメニューで使用可能なコンソールコマンド群を定義するためのデータアセット * AGameDebugMenuManager によって参照され、ユーザーがゲーム中にデバッグUIを通じてコマンドを実行できるようにするものです * コマンド情報はINIファイルで設定されたものを一括追加、他アセットのマージなど可能 */ UCLASS(Const, BlueprintType) class GAMEDEBUGMENU_API UGDMConsoleCommandSetAsset : public UDataAsset { GENERATED_BODY() public: /** コンソールコマンド名 */ UPROPERTY(EditAnywhere, Category = "ConsoleCommand") TArray ConsoleCommandNames; /** コンソールコマンドグループ名(同時に複数コマンド実行) */ UPROPERTY(EditAnywhere, Category = "ConsoleCommand") TArray ConsoleCommandGroups; /** コンソールコマンドペア名(トグルで2種のコマンド実行) */ UPROPERTY(EditAnywhere, Category = "ConsoleCommand") TArray ConsoleCommandPairs; /** コンソールコマンドナンバー(数値設定コマンド実行) */ UPROPERTY(EditAnywhere, Category = "ConsoleCommand") TArray ConsoleCommandNumbers; /** エディターでのみ追加されるコンソールコマンド名 */ UPROPERTY(EditAnywhere, Category = "ConsoleCommand") TArray EditorOnlyConsoleCommandNames; /** エディターでのみ追加されるコンソールコマンドグループ名 */ UPROPERTY(EditAnywhere, Category = "ConsoleCommand") TArray EditorOnlyConsoleCommandGroups; /** エディターでのみ追加されるコンソールコマンドペア名 */ UPROPERTY(EditAnywhere, Category = "ConsoleCommand") TArray EditorOnlyConsoleCommandPairs; /** エディターでのみ追加されるコンソールコマンドペア名 */ UPROPERTY(EditAnywhere, Category = "ConsoleCommand") TArray EditorOnlyConsoleCommandNumbers; /** コンソールコマンドのカテゴリ名表示順 */ UPROPERTY(EditAnywhere, EditFixedSize, Category = "ConsoleCommand") TArray OrderConsoleCommandCategoryTitles; public: #if WITH_EDITORONLY_DATA UPROPERTY(EditAnywhere, Category = "Editor") TArray CategoryIndexList; /** マージ対象の外部アセット */ UPROPERTY(EditAnywhere, Category = "Editor") TArray MergeSourceAssets; #endif /** * INIファイルで設定されたコマンド郡をこのアセットにセットする */ UFUNCTION(CallInEditor, Category = "Editor") void SetupCommandNames(); /** * INIファイルで設定されたコマンドのカテゴリ順をこのアセットにセットする */ UFUNCTION(CallInEditor, Category = "Editor") void SetupOrderConsoleCommandCategoryTitles(); /** * アセットをマージする */ UFUNCTION(CallInEditor, Category = "Editor") void MergeFromSourceAssets(); public: /** * コンソールコマンドメニューのカテゴリ名を取得 */ UFUNCTION(BlueprintCallable, BlueprintPure=false) TArray GetOrderConsoleCommandCategoryTitle() const; /** * コンソールコマンドの情報取得 */ UFUNCTION(BlueprintCallable, BlueprintPure=false) bool GetConsoleCommandNameByArrayIndex(const int32 ArrayIndex, FGDMConsoleCommandSingle& Out) const; /** * コンソールコマンド数 */ UFUNCTION(BlueprintPure) int32 GetNumConsoleCommandNames() const; /** * コンソールコマンドグループの情報取得 */ UFUNCTION(BlueprintCallable, BlueprintPure=false) bool GetConsoleCommandGroupByArrayIndex(const int32 ArrayIndex, FGDMConsoleCommandGroup& Out) const; /** * コンソールコマンドグループ数 */ UFUNCTION(BlueprintPure) int32 GetNumConsoleCommandGroups() const; /** * コンソールコマンドペアの情報取得 */ UFUNCTION(BlueprintCallable, BlueprintPure=false) bool GetConsoleCommandPairByArrayIndex(const int32 ArrayIndex, FGDMConsoleCommandPair& Out) const; /** * コンソールコマンドペア数 */ UFUNCTION(BlueprintPure) int32 GetNumConsoleCommandPairs() const; /** * コンソールコマンドナンバーの情報取得 */ UFUNCTION(BlueprintCallable, BlueprintPure=false) bool GetConsoleCommandNumberByArrayIndex(const int32 ArrayIndex, FGDMConsoleCommandNumber& Out) const; /** * コンソールコマンドナンバー数 */ UFUNCTION(BlueprintPure) int32 GetNumConsoleCommandNumbers() const; UFUNCTION(BlueprintCallable, BlueprintPure=false) bool FindConsoleCommandSingleById(const FString& CommandId, FGDMConsoleCommandSingle& Out) const; UFUNCTION(BlueprintCallable, BlueprintPure=false) bool FindConsoleCommandGroupById(const FString& CommandId, FGDMConsoleCommandGroup& Out) const; UFUNCTION(BlueprintCallable, BlueprintPure=false) bool FindConsoleCommandPairById(const FString& CommandId, FGDMConsoleCommandPair& Out) const; UFUNCTION(BlueprintCallable, BlueprintPure=false) bool FindConsoleCommandNumberById(const FString& CommandId, FGDMConsoleCommandNumber& Out) const; }; ``` `Plugin/GameDebugMenu/Source/GameDebugMenu/Public/Data/GameDebugMenuManagerAsset.h`: ```h /** * Copyright (c) 2020 akihiko moroi * * This software is released under the MIT License. * (See accompanying file LICENSE.txt or copy at http://opensource.org/licenses/MIT) */ #pragma once #include "CoreMinimal.h" #include "GameDebugMenuTypes.h" #include "Engine/DataAsset.h" #include "GameDebugMenuManagerAsset.generated.h" class AGDMDebugCameraInput; class UGDMPlayerControllerProxyComponent; class UGameDebugMenuRootWidget; class UGameDebugMenuWidget; class UGDMFavoriteItemDefinition; /** * デバッグメニューの構成をまとめたデータアセット * このアセットは、GameDebugMenuManager によって参照され、 * 表示するデバッグメニューUIの構成・入力設定などメニュー全体の挙動を制御する設定を一元的に保持 */ UCLASS(Const) class GAMEDEBUGMENU_API UGameDebugMenuManagerAsset : public UDataAsset { GENERATED_BODY() public: /** メインとなるデバックメニューのWidgetクラス */ UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "Widget") TSubclassOf DebugMenuRootWidgetClass; /** 各メニューのWidget郡(Key=メニュー識別名, Value=メニュークラス) */ UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "Widget") TMap > DebugMenuClasses; /** 各メニューのUI登録順(メニュー識別名を指定する) */ UPROPERTY(EditAnywhere, BlueprintReadOnly,Category = "Widget") TArray DebugMenuRegistrationOrder; /** メインとなるデバックメニューのWidgetのZオーダー値 */ UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "Widget") int32 RootWidgetZOrder; /** DebugMenuManagerが作られたときに追加される InputMappingContext */ UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "Input") TArray AddInputMappingContextWhenCreateManager; /** DebugMenuを表示するときに追加される InputMappingContext */ UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "Input") TArray AddInputMappingContextWhenDebugMenuIsShow; /** デバックカメラ用のインプットアクター */ UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "Input") TSubclassOf DebugCameraInputClass; /** お気に入りデータの定義 */ UPROPERTY(EditAnywhere, Instanced, category = "Favorite") TArray> FavoriteItemDefinitions; /** PlayerControllerに自動追加されるコンポーネント */ UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "Other") TSubclassOf DebugMenuPCProxyComponentClass; /** True : デバックメニュー操作中ポーズする */ UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "Other") bool bGamePause; public: UGameDebugMenuManagerAsset(); }; ``` `Plugin/GameDebugMenu/Source/GameDebugMenu/Public/Data/GameDebugMenuMasterAsset.h`: ```h /** * Copyright (c) 2020 akihiko moroi * * This software is released under the MIT License. * (See accompanying file LICENSE.txt or copy at http://opensource.org/licenses/MIT) */ #pragma once #include "CoreMinimal.h" #include "Engine/DataAsset.h" #include "GameDebugMenuTypes.h" #include "GameDebugMenuMasterAsset.generated.h" class AGDMDebugReportRequester; class UGDMEnhancedInputComponent; class AGameDebugMenuManager; /** * デバッグメニュー全体で使用されるアセット情報をまとめたアセット * デバッグメニューに関係する構成要素への参照を一元化して保持 * UPrimaryDataAsset なのでPrimaryAssetTypesToScanに追加が必要。(GetPrimaryType参照) * UGameDebugMenuSettingsのMasterAssetNameにこのアセットを指定することで使用できる */ UCLASS(Const) class GAMEDEBUGMENU_API UGameDebugMenuMasterAsset : public UPrimaryDataAsset { GENERATED_BODY() public: /** 使用するマネージャークラス */ UPROPERTY(EditAnywhere, Category = "Manager") TArray> DebugMenuManagerClasses; /** メニューの入力処理をするコンポーネント */ UPROPERTY(EditAnywhere, Category = "Input") TSoftClassPtr DebugMenuInputComponentClass; /** バグレポート処理クラス */ UPROPERTY(EditAnywhere, Category = "ReportSettings") TMap> DebugReportRequesterClass; /** デバックメニュー用StringTable */ UPROPERTY(EditAnywhere, Category = "Localization") TMap GameDebugMenuStringTables; /** デバックメニューのUMG使用フォント */ UPROPERTY(EditAnywhere, Category = "Font", meta = (AllowedClasses = "/Script/Engine.Font", DisplayName = "Font Family")) FSoftObjectPath FontName; public: UGameDebugMenuMasterAsset(const FObjectInitializer& ObjectInitializer); virtual FPrimaryAssetId GetPrimaryAssetId() const override; static const FPrimaryAssetType& GetPrimaryType(); TSoftClassPtr GetGameDebugMenuManagerSoftClass(FString ClassName) const; }; ``` `Plugin/GameDebugMenu/Source/GameDebugMenu/Public/Favorite/GDMFavoriteItemDefinition.h`: ```h /** * Copyright (c) 2020 akihiko moroi * * This software is released under the MIT License. * (See accompanying file LICENSE.txt or copy at http://opensource.org/licenses/MIT) */ #pragma once #include "CoreMinimal.h" #include "UObject/Object.h" #include "GDMFavoriteItemDefinition.generated.h" class AGameDebugMenuManager; class UUserWidget; /** * お気に入り登録できるWidget情報を定義 */ UCLASS(Abstract, Blueprintable, EditInlineNew, CollapseCategories) class GAMEDEBUGMENU_API UGDMFavoriteItemDefinition : public UObject { GENERATED_BODY() public: /* お気に入り登録されるWidgetクラス */ UPROPERTY(EditAnywhere, BlueprintReadOnly) TSubclassOf FavoriteWidgetClass = nullptr; TWeakObjectPtr OwnerManager = nullptr; public: virtual UWorld* GetWorld() const override; UFUNCTION(BlueprintPure, Category = "GDM") AGameDebugMenuManager* GetOwnerManager() const; /** * この定義がサポートするWidgetか? */ UFUNCTION(BlueprintNativeEvent, Category = "GDM") bool IsSupportedWidget(UUserWidget* Widget) const; virtual bool IsSupportedWidget_Implementation(UUserWidget* Widget) const PURE_VIRTUAL(, return false;); /** * エクスポート処理 * @return Widgetを復元するために必要な識別キーを返す */ UFUNCTION(BlueprintNativeEvent, Category = "GDM") FString ExportToFavoriteKey(UUserWidget* TargetWidget); virtual FString ExportToFavoriteKey_Implementation(UUserWidget* TargetWidget) PURE_VIRTUAL(, return FString();); /** * インポート処理+Widget生成 * @param FavoriteSaveKey - Widgetを復元するために必要な識別キー */ UFUNCTION(BlueprintNativeEvent, Category = "GDM") UUserWidget* CreateWidgetFromFavoriteData(const FString& FavoriteSaveKey); virtual UUserWidget* CreateWidgetFromFavoriteData_Implementation(const FString& FavoriteSaveKey) PURE_VIRTUAL(, return nullptr;); }; ``` `Plugin/GameDebugMenu/Source/GameDebugMenu/Public/Favorite/GDMFavoriteSystemComponent.h`: ```h /** * Copyright (c) 2020 akihiko moroi * * This software is released under the MIT License. * (See accompanying file LICENSE.txt or copy at http://opensource.org/licenses/MIT) */ #pragma once #include "CoreMinimal.h" #include "GameDebugMenuTypes.h" #include "Components/ActorComponent.h" #include "GDMFavoriteSystemComponent.generated.h" class AGameDebugMenuManager; class UGDMFavoriteItemDefinition; class UGameDebugMenuManagerAsset; class UUserWidget; class UGDMPropertyJsonSystemComponent; /** * メニューのお気に入りシステム制御コンポーネント */ UCLASS() class GAMEDEBUGMENU_API UGDMFavoriteSystemComponent : public UActorComponent { GENERATED_BODY() /** お気に入り情報 */ UPROPERTY() TArray> CachedFavoriteDefinitions; public: UGDMFavoriteSystemComponent(); protected: UGDMPropertyJsonSystemComponent* GetPropertyJsonSystemComponent() const; public: virtual void Initialize(const UGameDebugMenuManagerAsset* InMenuAsset); /** * お気に入りに追加/削除を行う * @param TargetWidget - お気に入り対象のWidget * @return true 追加/削除を行った false お気に入り処理の対象外のWidgetだった */ UFUNCTION(BlueprintCallable) virtual bool ToggleAddOrRemoveFavorite(UUserWidget* TargetWidget); /** * お気に入りに追加する */ UFUNCTION(BlueprintCallable) virtual void AddFavorite(UUserWidget* TargetWidget); /** * お気に入りから削除 */ UFUNCTION(BlueprintCallable) virtual void RemoveFavorite(UUserWidget* TargetWidget); /** * お気に入り処理ができるWidgetか? */ UFUNCTION(BlueprintPure) virtual bool CanFavorite(UUserWidget* TargetWidget); /** * お気に入り中か? */ UFUNCTION(BlueprintPure) virtual bool IsFavorited(UUserWidget* TargetWidget); /** * お気に入りから全削除をする */ UFUNCTION(BlueprintCallable) virtual void ClearAllFavorites(); /** * 指定のお気に入り情報が現在の情報と一致してるか? * @return true 完全一致 */ UFUNCTION(BlueprintPure) bool IsFavoriteListEqualTo(const TArray& OtherList) const; /** * お気に入り情報から関連するWidgetを生成し返す */ UFUNCTION(BlueprintCallable) virtual UUserWidget* CreateFavoriteWidgetFromEntry(const FGDMFavoriteEntry& Entry); }; ``` `Plugin/GameDebugMenu/Source/GameDebugMenu/Public/GameDebugMenu.h`: ```h /** * Copyright (c) 2020 akihiko moroi * * This software is released under the MIT License. * (See accompanying file LICENSE.txt or copy at http://opensource.org/licenses/MIT) */ #pragma once #include "Modules/ModuleManager.h" class GAMEDEBUGMENU_API FGameDebugMenuModule : public IModuleInterface { public: /* Begin IModuleInterface */ virtual void StartupModule() override; virtual void ShutdownModule() override; /* End IModuleInterface */ }; ``` `Plugin/GameDebugMenu/Source/GameDebugMenu/Public/GameDebugMenuFunctions.h`: ```h /** * Copyright (c) 2020 akihiko moroi * * This software is released under the MIT License. * (See accompanying file LICENSE.txt or copy at http://opensource.org/licenses/MIT) */ #pragma once #include "Kismet/BlueprintFunctionLibrary.h" #include "GameDebugMenuManager.h" #include "GameDebugMenuTypes.h" #include "GameDebugMenuFunctions.generated.h" class UWidget; UCLASS() class GAMEDEBUGMENU_API UGameDebugMenuFunctions : public UBlueprintFunctionLibrary { GENERATED_BODY() static TArray RegisterPendingProperties; static TArray RegisterPendingFunctions; static FDelegateHandle ActorSpawnedDelegateHandle; static bool bDisableGameDebugMenu; public: UGameDebugMenuFunctions(const FObjectInitializer& ObjectInitializer); public: static void RegisterGameDebugMenuManagerInstance(AGameDebugMenuManager* RegisterManager); static void UnregisterGameDebugMenuManagerInstance(AGameDebugMenuManager* UnregisterManager); public: /** * 生成。ロードしてない場合は同期ロードされるので注意 * @param PlayerController - メニューを操作するローカルのプレイヤーコントローラー * @param DebugMenuManagerClassName - 生成するマネージャークラス名(BP_GDM_Manager) * @return true: 正常終了 false: デバックメニューは使用できない */ UFUNCTION(BlueprintCallable, Category = "GDM|Functions", meta = (Keywords = "DebugMenu GDM")) static bool TryCreateDebugMenuManager(APlayerController* PlayerController, FString DebugMenuManagerClassName); /** * 削除 */ UFUNCTION(BlueprintCallable, Category = "GDM|Functions", meta = (Keywords = "DebugMenu GDM")) static bool DestroyDebugMenuManager(APlayerController* PlayerController); /** * 取得(Managerの参照をもつことになるので使用する場合は注意) */ UFUNCTION(BlueprintPure, Category = "GDM|Functions", meta = (Keywords = "DebugMenu GDM", WorldContext = "WorldContextObject", AdvancedDisplay = "bCheckInitialize")) static AGameDebugMenuManager* GetGameDebugMenuManager(const UObject* WorldContextObject, bool bCheckInitialize = true); /** * 取得(Managerの参照をもつことになるので使用する場合は注意) */ UFUNCTION(BlueprintPure, Category = "GDM|Functions", meta = (Keywords = "DebugMenu GDM")) static AGameDebugMenuManager* TryGetGameDebugMenuManagerFromPlayerController(const APlayerController* PlayerController); /** * 表示 * @return True: 処理の正常終了 */ UFUNCTION(BlueprintCallable, BlueprintCosmetic, Category = "GDM|Functions", meta = (Keywords = "DebugMenu GDM", WorldContext = "WorldContextObject")) static bool ShowDebugMenu(UObject* WorldContextObject); /** * 非表示 * @return True: 処理の正常終了 */ UFUNCTION(BlueprintCallable, BlueprintCosmetic, Category = "GDM|Functions", meta = (Keywords = "DebugMenu GDM", WorldContext = "WorldContextObject")) static bool HideDebugMenu(UObject* WorldContextObject); /** * (非)表示に切り替える * @return True: 処理の正常終了 */ UFUNCTION(BlueprintCallable, BlueprintCosmetic, Category = "GDM|Functions", meta = (Keywords = "DebugMenu GDM", WorldContext = "WorldContextObject")) static bool ToggleDebugMenu(UObject* WorldContextObject); /** * True: 表示中 False:非表示 */ UFUNCTION(BlueprintCallable, BlueprintCosmetic, Category = "GDM|Functions", meta = (Keywords = "DebugMenu GDM", WorldContext = "WorldContextObject")) static bool IsShowingDebugMenu(UObject* WorldContextObject); /** * RootになるWidget取得(Widgetの参照をもつことになるので使用する場合は注意) */ UFUNCTION(BlueprintPure, BlueprintCosmetic, Category = "GDM|Functions", meta = (Keywords = "DebugMenu GDM", WorldContext = "WorldContextObject")) static UGameDebugMenuRootWidget* GetGameDebugMenuRootWidget(const UObject* WorldContextObject); /** * DebugMenuに表示させるオブジェクトのプロパティを登録する * @param TargetObject - プロパティを所持してる対象のオブジェクト * @param PropertyUIConfigInfo - UIでの設定情報 * @param PropertyName - プロパティ名 * @param CategoryKey - カテゴリ * @param PropertySaveKey - プロパティデータ保存用の識別キー * @param DisplayPropertyName - UI上のプロパティ名,何も指定しなければ「PropertyName」で表示 * @param Description - 説明文 * @param DisplayPriority - リスト追加時の表示優先度(降順) * @return True: 登録成功 False: 何らかの要因で失敗した(ログに出力されてます) */ UFUNCTION(BlueprintCallable, BlueprintCosmetic, Category = "GDM|Functions", meta = (Keywords = "DebugMenu GDM", AdvancedDisplay = "5")) static bool RegisterGDMObjectProperty(UObject* TargetObject, const FGDMPropertyUIConfigInfo PropertyUIConfigInfo, const FName PropertyName, const FGDMGameplayCategoryKey CategoryKey, const FString PropertySaveKey, const FText DisplayPropertyName,const FText Description,const int32 DisplayPriority); /** * DebugMenuに表示させるオブジェクトの関数(カスタムイベント)を登録する * @param TargetObject - 関数(カスタムイベント)を所持してる対象のオブジェクト * @param FunctionName - 関数(カスタムイベント)名 * @param CategoryKey - カテゴリ * @param FunctionSaveKey - 関数保存用の識別キー * @param DisplayFunctionName - UI上の関数(カスタムイベント)名,何も指定しなければ「FunctionName」 * @param Description - 説明文 * @param DisplayPriority - リスト追加時の表示優先度(降順) * @return True: 登録成功 False: 何らかの要因で失敗した(ログに出力されてます) * @note ここで登録できるのは引数0の関数(カスタムイベント)のみ */ UFUNCTION(BlueprintCallable, BlueprintCosmetic, Category = "GDM|Functions", meta = (Keywords = "DebugMenu GDM", AdvancedDisplay = "3")) static bool RegisterGDMObjectFunction(UObject* TargetObject, FName FunctionName, const FGDMGameplayCategoryKey CategoryKey, const FString FunctionSaveKey, const FText DisplayFunctionName,const FText Description, const int32 DisplayPriority); /** * 対象のオブジェクトが登録したプロパティ&関数を解除する */ UFUNCTION(BlueprintCallable, BlueprintCosmetic, Category = "GDM|Functions", meta = (Keywords = "DebugMenu GDM")) static void UnregisterGDMObject(UObject* TargetObject); /** * 登録済みプロパティ情報を取得する */ UFUNCTION(BlueprintCallable, BlueprintCosmetic, Category = "GDM|Functions", meta = (Keywords = "DebugMenu GDM", WorldContext = "WorldContextObject")) static UObject* GetGDMObjectProperty(UObject* WorldContextObject,const int32 Index, FGDMGameplayCategoryKey& OutCategoryKey, FString& OutPropertySaveKey, FText& OutDisplayPropertyName, FText& OutDescription, FName& OutPropertyName, EGDMPropertyType& OutPropertyType, FString& OutEnumPathName, FGDMPropertyUIConfigInfo& PropertyUIConfigInfo); /** * 登録済み関数(カスタムイベント)を取得する */ UFUNCTION(BlueprintCallable, BlueprintCosmetic, Category = "GDM|Functions", meta = (Keywords = "DebugMenu GDM", WorldContext = "WorldContextObject")) static UObject* GetGDMObjectFunction(UObject* WorldContextObject,const int32 Index, FGDMGameplayCategoryKey& OutCategoryKey, FString& OutFunctionSaveKey, FText& OutDisplayFunctionName, FText& OutDescription, FName& OutFunctionName); /** * 登録済みプロパティ数を取得 */ UFUNCTION(BlueprintPure, BlueprintCosmetic, Category = "GDM|Functions", meta = (Keywords = "DebugMenu GDM", WorldContext = "WorldContextObject")) static int32 GetGDMNumObjectProperties(UObject* WorldContextObject); /** * 登録済み関数(カスタムイベント)数を取得 */ UFUNCTION(BlueprintPure, BlueprintCosmetic, Category = "GDM|Functions", meta = (Keywords = "DebugMenu GDM", WorldContext = "WorldContextObject")) static int32 GetGDMNumObjectFunctions(UObject* WorldContextObject); /** * 登録済みプロパティが使用できるか確認する * @return True すべて問題なし False 1つ以上使用できないものがあった */ UFUNCTION(BlueprintCallable, BlueprintCosmetic, Category = "GDM|Functions", meta = (Keywords = "DebugMenu GDM", WorldContext = "WorldContextObject")) static bool VerifyGDMNumObjectProperties(UObject* WorldContextObject); /** * 登録済み関数が使用できるか確認する * @return True すべて問題なし False 1つ以上使用できないものがあった */ UFUNCTION(BlueprintCallable, BlueprintCosmetic, Category = "GDM|Functions", meta = (Keywords = "DebugMenu GDM", WorldContext = "WorldContextObject")) static bool VerifyGDMNumObjectFunctions(UObject* WorldContextObject); /** * Gameplayメニューのカテゴリ名を取得 */ UFUNCTION(BlueprintCallable, Category = "GDM|Functions", meta = (Keywords = "DebugMenu GDM", WorldContext = "WorldContextObject")) static TArray GetGDMOrderGameplayCategoryTitle(UObject* WorldContextObject); /** * 無効なIndex */ UFUNCTION(BlueprintPure, Category = "GDM|Functions", meta = (Keywords = "DebugMenu GDM")) static int32 GetGDMInvalidIndex(); /** * オブジェクト名を取得する * 内部ではEditorならGetDisplayNameそれ以外でGetObjectNameを呼ぶ */ UFUNCTION(BlueprintPure,Category = "GDM|Functions",meta = ( Keywords = "DebugMenu GDM" )) static FString GetGDMObjectName(UObject* TargetObject); /** * デバッグメニュー操作用のInputComponentを登録する */ UFUNCTION(BlueprintCallable, BlueprintCosmetic, Category = "GDM|Functions", meta = (Keywords = "DebugMenu GDM", WorldContext = "WorldContextObject")) static void RegisterInputComponentForGameDebugMenu(UObject* WorldContextObject, UInputComponent* InputComponent); /** * デバッグメニュー操作用のInputComponentを解除する */ UFUNCTION(BlueprintCallable, BlueprintCosmetic, Category = "GDM|Functions", meta = (Keywords = "DebugMenu GDM", WorldContext = "WorldContextObject")) static void UnregisterInputComponentForGameDebugMenu(UObject* WorldContextObject, UInputComponent* InputComponent); /** * DebugReport用UIを表示する * @note 通常の開閉は最後に操作したMenuになるがこっちはなからずDebugReport用UIを表示されキャプチャもDebugMenuが含まれない */ UFUNCTION(BlueprintCallable, BlueprintCosmetic, Category = "GDM|Functions", meta = (Keywords = "DebugMenu GDM", WorldContext = "WorldContextObject")) static bool ShowDebugReport(UObject* WorldContextObject); /** * DebugReport用UIが対応するツールを取得 */ UFUNCTION(BlueprintPure, BlueprintCosmetic, Category = "GDM|Functions", meta = (Keywords = "DebugMenu GDM")) static EGDMProjectManagementTool GetGDMSelectedProjectManagementTool(); /** * ビルド構成を文字列で取得 * @note EBuildConfiguration(Engine\Source\Runtime\Core\Public\GenericPlatform\GenericPlatformMisc.h)参照 */ UFUNCTION(BlueprintPure, BlueprintCosmetic, Category = "GDM|Functions", meta = (Keywords = "DebugMenu GDM")) static FString GetGDMBuildConfigurationString(); /** * エンジンのビルドバージョンを取得 */ UFUNCTION(BlueprintPure, BlueprintCosmetic, Category = "GDM|Functions", meta = (Keywords = "DebugMenu GDM")) static FString GetGDMBuildVersionString(); /** * プロジェクトバージョンの取得 */ UFUNCTION(BlueprintPure, BlueprintCosmetic, Category = "GDM|Functions", meta = (Keywords = "DebugMenu GDM")) static FString GetGDMProjectVersionString(); /** * デバッグメニュー用のログカテゴリを使用したPrintString */ UFUNCTION(BlueprintCallable, BlueprintCosmetic, Category = "GDM|Functions", meta = (Keywords = "DebugMenu GDM", WorldContext = "WorldContextObject", DisplayName = "PrintLogScreenGDM")) static void PrintLogScreen(UObject* WorldContextObject, const FString& InString, float Duration = 0.2f, bool bPrintToLog = false); /** * デバッグメニュー用のStringTableから文字列を取得する * @param StringKey - DebugMenuのStringTableのキー * @param OutString - キー対応する文字列 * @return true:成功。キーに合う文字列を取得 false:失敗。キーが存在しないかマネージャーが生成されてない */ UFUNCTION(BlueprintCallable, Category = "GDM|Functions", meta = (Keywords = "DebugMenu GDM", WorldContext = "WorldContextObject")) static bool GetDebugMenuString(UObject* WorldContextObject, const FString StringKey, FString& OutString); /** * デバッグメニューから切り替えできる言語を取得(エンジン内の言語切り替え) */ UFUNCTION(BlueprintPure, BlueprintCosmetic, Category = "GDM|Functions", meta = (Keywords = "DebugMenu GDM")) static TArray GetGDMCultureList(); /** * デバッグメニューの現在使用中の言語を取得 */ UFUNCTION(BlueprintPure, Category = "GDM|Functions", meta = (Keywords = "DebugMenu GDM", WorldContext = "WorldContextObject")) static FName GetCurrentDebugMenuLanguage(UObject* WorldContextObject); /** * デバッグメニューで使用できる言語を取得 */ UFUNCTION(BlueprintCallable, Category = "GDM|Functions", meta = (Keywords = "DebugMenu GDM")) static TArray GetDebugMenuLanguageKeys(); /** * デバッグメニューで使用する共通の改行判定文字を取得 */ UFUNCTION(BlueprintPure, Category = "GDM|Functions", meta = (Keywords = "DebugMenu GDM")) static FString GetDebugMenuLineBreakString(); /** 単体コマンドから識別子を構築 */ UFUNCTION(BlueprintCallable, Category = "ConsoleCommand") static FString BuildConsoleCommandId_FromSingle(const FGDMConsoleCommandSingle& Command); /** グループコマンドから識別子を構築 */ UFUNCTION(BlueprintCallable, Category = "ConsoleCommand") static FString BuildConsoleCommandId_FromGroup(const FGDMConsoleCommandGroup& Command); /** ペアコマンドから識別子を構築 */ UFUNCTION(BlueprintCallable, Category = "ConsoleCommand") static FString BuildConsoleCommandId_FromPair(const FGDMConsoleCommandPair& Command); /** 数値指定コマンドから識別子を構築 */ UFUNCTION(BlueprintCallable, Category = "ConsoleCommand") static FString BuildConsoleCommandId_FromNumber(const FGDMConsoleCommandNumber& Command); public: UFUNCTION(BlueprintPure, meta = (DisplayName = "Equal (GDMMenuCategoryKey)", CompactNodeTitle = "=="), Category = "GDM|Functions") static bool EqualEqual_GDMMenuCategoryKey(const FGDMMenuCategoryKey& A, const FGDMMenuCategoryKey& B); UFUNCTION(BlueprintPure, meta = (DisplayName = "NotEqual (GDMMenuCategoryKey)", CompactNodeTitle = "!="), Category = "GDM|Functions") static bool NotEqual_GDMMenuCategoryKey(const FGDMMenuCategoryKey& A, const FGDMMenuCategoryKey& B); UFUNCTION(BlueprintPure, meta = (DisplayName = "ToByte (GDMMenuCategoryKey)", CompactNodeTitle = "->", BlueprintAutocast), Category = "GDM|Functions") static uint8 Conv_GDMMenuCategoryKeyToByte(const FGDMMenuCategoryKey& Key); UFUNCTION(BlueprintPure, meta = (DisplayName = "ToGDMMenuCategoryKey (Byte)", CompactNodeTitle = "->", BlueprintAutocast), Category = "GDM|Functions") static FGDMMenuCategoryKey Conv_ByteToGDMMenuCategoryKey(const uint8& Key); private: static void OnActorSpawnedClientWaitManager(AGameDebugMenuManager* SpawnDebugMenuManager); static void OnActorSpawnedServer(AActor* SpawnActor); static void ShowDebugConsoleCommand(); static void HideDebugConsoleCommand(); static void ToggleDebugConsoleCommand(); static void ToggleInputSystemLog(); }; ``` `Plugin/GameDebugMenu/Source/GameDebugMenu/Public/GameDebugMenuManager.h`: ```h /** * Copyright (c) 2020 akihiko moroi * * This software is released under the MIT License. * (See accompanying file LICENSE.txt or copy at http://opensource.org/licenses/MIT) */ #pragma once #include "CoreMinimal.h" #include "GameFramework/Actor.h" #include "GameDebugMenuTypes.h" #include "GameDebugMenuManager.generated.h" class UGDMConsoleCommandSetAsset; class UGDMFavoriteSystemComponent; class UGDMConsoleCommandValueProviderComponent; class UGDMLocalizeStringComponent; class UGDMSaveSystemComponent; class UGDMPropertyJsonSystemComponent; class UGDMListenerComponent; class UGameDebugMenuWidget; class UGDMInputSystemComponent; class UGDMScreenshotRequesterComponent; class UGameDebugMenuManagerAsset; class UGameDebugMenuRootWidget; class UGDMPlayerControllerProxyComponent; class AGDMDebugReportRequester; class FGDMOutputDevice; /** * デバックメニュー管理マネージャー(生成はTryCreateDebugMenuManagerで) */ UCLASS(Blueprintable, BlueprintType, notplaceable) class GAMEDEBUGMENU_API AGameDebugMenuManager : public AActor { GENERATED_BODY() protected: /** DebugMenuの入力操作を制御するコンポーネント */ UPROPERTY(VisibleAnywhere, Category = "GDM", meta = (AllowPrivateAccess = "true")) TObjectPtr DebugMenuInputSystemComponent; /** DebugMenuで使用するスクリーンショットを制御するコンポーネント */ UPROPERTY(VisibleAnywhere, Category = "GDM", meta = (AllowPrivateAccess = "true")) TObjectPtr ScreenshotRequesterComponent; UPROPERTY(VisibleAnywhere, Category = "GDM", meta = (AllowPrivateAccess = "true")) TObjectPtr PropertyJsonSystemComponent; UPROPERTY(VisibleAnywhere, Category = "GDM", meta = (AllowPrivateAccess = "true")) TObjectPtr SaveSystemComponent; UPROPERTY(VisibleAnywhere, Category = "GDM", meta = (AllowPrivateAccess = "true")) TObjectPtr FavoriteSystemComponent; UPROPERTY(VisibleAnywhere, Category = "GDM", meta = (AllowPrivateAccess = "true")) TObjectPtr ConsoleCommandValueProviderComponent; UPROPERTY(VisibleAnywhere, Category = "GDM", meta = (AllowPrivateAccess = "true")) TObjectPtr LocalizeStringComponent; /** DebugMenuの各イベント検知コンポーネント */ UPROPERTY(VisibleAnywhere, Category = "GDM", meta = (AllowPrivateAccess = "true")) TObjectPtr ListenerComponent; FTimerHandle InitializeManagerHandle; bool bInitializedManager = false; /** BeginPlay時点でOwnerが未確定なことがあるため、ローカル判定ができるまで短時間リトライする */ FTimerHandle LocalBeginPlayRetryHandle; bool bLocalBeginPlayInitialized = false; /** デバックメニュー用UIアセット */ UPROPERTY(EditAnywhere, Category = "GDM") TObjectPtr MenuAsset; /** コンソールコマンド設定アセット */ UPROPERTY(EditAnywhere, Category = "GDM") TObjectPtr ConsoleCommandSetAsset; /** True:UI表示中 */ bool bShowDebugMenu; /** メニュー開く前のポーズ判定 */ bool bCachedGamePaused; /** メニュー開く前のマウスカーソル判定 */ bool bCachedShowMouseCursor; /** スクリーンキャプチャ後レポート用UIを開く */ bool bWaitToCaptureBeforeOpeningDebugReportMenu; /** メニュー開く前のナビゲーション情報 */ TArray> CachedNavigationConfigs; /** 登録済みプロパティ群 */ TArray> ObjectProperties; /** 登録済み関数群 */ TArray> ObjectFunctions; /** Viewport上に追加されてるメインWidget */ UPROPERTY(Transient) TObjectPtr DebugMenuRootWidget; /** 生成した各メニューWidgetのインスタンス */ UPROPERTY(Transient,BlueprintReadOnly, Category = "GDM") TMap> DebugMenuInstances; /** Viewport上に追加されてるメニューWidgetのインスタンス */ UPROPERTY(Transient) TArray> ViewportDebugMenuWidgets; /** DebugMenuのログデバイス(レポート送信用) */ TSharedPtr OutputLog; public: AGameDebugMenuManager(const FObjectInitializer& ObjectInitializer); protected: virtual void BeginPlay() override; public: virtual void EndPlay(const EEndPlayReason::Type EndPlayReason) override; virtual void Tick(float DeltaTime) override; public: UFUNCTION(BlueprintPure) UGDMInputSystemComponent* GetDebugMenuInputSystemComponent() const; UFUNCTION(BlueprintPure) UGDMScreenshotRequesterComponent* GetScreenshotRequesterComponent() const; UFUNCTION(BlueprintPure) UGDMPropertyJsonSystemComponent* GetPropertyJsonSystemComponent() const; UFUNCTION(BlueprintPure) UGDMSaveSystemComponent* GetSaveSystemComponent() const; UFUNCTION(BlueprintPure) UGDMFavoriteSystemComponent* GetFavoriteSystemComponent() const; UFUNCTION(BlueprintPure) UGDMConsoleCommandValueProviderComponent* GetConsoleCommandValueProviderComponent() const; UFUNCTION(BlueprintPure) UGDMLocalizeStringComponent* GetLocalizeStringComponent() const; UFUNCTION(BlueprintPure) UGDMListenerComponent* GetListenerComponent() const; UFUNCTION(BlueprintPure, Category = "GDM") bool IsInitializedManager() const; protected: /** * マネージャーの初期化 */ virtual void OnInitializeManager(); /** * マネージャーの初期化処理が完了できる状態か判断する関数 * - false を返している間は初期化を完了しない * - true その後OnInitializeManagerBPが実行される */ UFUNCTION(BlueprintCallable, BlueprintNativeEvent, Category="GDM", meta=(DisplayName="IsReadyToInitializeManager", ScriptName="IsReadyToInitializeManager")) bool IsReadyToInitializeManager(); virtual bool IsReadyToInitializeManager_Implementation() { return true; } UFUNCTION(BlueprintImplementableEvent, Category="GDM", meta=(DisplayName="OnInitializeManager", ScriptName="OnInitializeManager")) void OnInitializeManagerBP(); /** * UIのルートWidgetの生成 */ virtual void CreateDebugMenuRootWidget(); /** * Navigation(無)有効(Menu開く前の状態にする) */ virtual void EnabledNavigationConfigs(); virtual void DisabledNavigationConfigs(); /** * マウスカーソルのオン・オフ(Menu開く前の状態にする) */ void EnableShowMouseCursorFlag(APlayerController* PlayerController); void RestoreShowMouseCursorFlag(APlayerController* PlayerController) const; /** * メニュー操作時のゲームポーズのオン・オフ(処理するかは「bGamePause」で判断) */ void TryEnableGamePause(); void RestoreGamePause() const; /** * スクショ処理終了後呼ばれる */ UFUNCTION() virtual void OnScreenshotRequestProcessed(); public: /** * コンソールコマンドの実行 */ virtual void ExecuteConsoleCommand(const FString& Command, APlayerController* PC); /** * DebugMenuの表示する */ virtual bool ShowDebugMenu(bool bWaitToCaptureBeforeOpeningMenuFlag = false); /** * DebugMenuを非表示にする */ virtual void HideDebugMenu(); /** * True:表示中 False:非表示 */ virtual bool IsShowingDebugMenu(); /** * オーナーのPlayerControllerを取得 */ UFUNCTION(BlueprintPure, Category = "GDM") APlayerController* GetOwnerPlayerController() const; /** * メインとなるWidget取得 */ virtual UGameDebugMenuRootWidget* GetDebugMenuRootWidget(); /** * 各Debug画面となるWidgetクラスのキーリストを取得 */ UFUNCTION(BlueprintCallable, Category = "GDM") int32 GetAllDebugMenuKeys(TArray& OutKeys); /** * キーと紐付いた各Debug画面となるWidgetクラスを取得 */ UFUNCTION(BlueprintCallable, Category = "GDM") TSubclassOf GetDebugMenuWidgetClass(const FString& Key); /** * 各Debug画面となるWidgetのインスタンスからキーを取得 */ UFUNCTION(BlueprintCallable, Category = "GDM") FString GetDebugMenuWidgetKey(const UGameDebugMenuWidget* Widget); /** * 生成済みのDebug画面となるWidgetを取得 */ UFUNCTION(BlueprintCallable, Category = "GDM") bool GetDebugMenuWidgetInstances(TArray& OutInstances); /** * ゲーム内のログ取得 */ virtual void GetOutputLogString(FString& OutLog, const FString& Separator); /** * ゲーム中実行したコンソールコマンドの履歴を取得 */ virtual void GetOutputCommandHistoryString(TArray& OutCommandHistory); UFUNCTION(BlueprintCallable, Category = "GDM") virtual void ClearCommandHistory(); virtual EGDMPropertyType GetPropertyType(const FProperty* TargetProperty) const; virtual bool RegisterObjectProperty(UObject* TargetObject, const FName PropertyName, const FGDMGameplayCategoryKey& CategoryKey, const FString& PropertySaveKey, const FText& DisplayPropertyName, const FText& Description, const FGDMPropertyUIConfigInfo& PropertyUIConfigInfo, const int32& DisplayPriority); virtual bool RegisterObjectFunction(UObject* TargetObject, const FName FunctionName, const FGDMGameplayCategoryKey& CategoryKey, const FString& FunctionSaveKey, const FText& DisplayFunctionName, const FText& Description, const int32& DisplayPriority); virtual UObject* GetObjectProperty(const int32 Index, FGDMGameplayCategoryKey& OutCategoryKey, FString& OutPropertySaveKey, FText& OutDisplayPropertyName, FText& OutDescription, FName& OutPropertyName, EGDMPropertyType& OutPropertyType, FString& OutEnumPathName, FGDMPropertyUIConfigInfo& OutPropertyUIConfigInfo); virtual void RemoveObjectProperty(const int32 Index); virtual UObject* GetObjectFunction(const int32 Index, FGDMGameplayCategoryKey& OutCategoryKey, FString& OutFunctionSaveKey, FText& OutDisplayFunctionName, FText& OutDescription, FName& OutFunctionName); void RemoveObjectFunction(const int32 Index); int32 GetNumObjectProperties() const; int32 GetNumObjectFunctions() const; UFUNCTION(BlueprintCallable, BlueprintPure=false) UObject* TryGetObjectProperty(const FString& InPropertySaveKey, const FString& InPropertyName, FGDMGameplayCategoryKey& OutCategoryKey, FText& OutDisplayPropertyName, FText& OutDescription, EGDMPropertyType& OutPropertyType, FString& OutEnumPathName, FGDMPropertyUIConfigInfo& OutPropertyUIConfigInfo) const; UFUNCTION(BlueprintCallable, BlueprintPure=false) UObject* TryGetObjectFunction(const FString& InFunctionSaveKey, const FString& InFunctionName, FGDMGameplayCategoryKey& OutCategoryKey, FText& OutDisplayFunctionName, FText& OutDescription) const; /** * ProxyComponentを対象PlayerControllerに追加する */ virtual void AddDebugMenuPCProxyComponent(APlayerController* PlayerController); /** * DebugMenuの入力無視フラグの設定 * (AControllerのSetIgnoreMoveInputと同様呼び出し回数を揃える必要がある) * * @param bNewInput true : DebugMenuの入力が無視されるようになる false : フラグを1つ解除する */ UFUNCTION(BlueprintCallable, Category = "GDM|Input") virtual void SetIgnoreInput(bool bNewInput); /** * DebugMenuの入力無視状態フラグをリセットする */ UFUNCTION(BlueprintCallable, Category = "GDM|Input") virtual void ResetIgnoreInput(); /** * DebugMenuの入力を無視する状態になっているか? * * @return True : 無視状態 False : 入力可能 */ UFUNCTION(BlueprintCallable, Category = "GDM|Input") virtual bool IsInputIgnored() const; /** * DebugMenuの言語を変更する */ UFUNCTION(BlueprintCallable, Category = "GDM") virtual void ChangeDebugMenuLanguage(FName LanguageKey, bool bForcedUpdate); /** * Viewport上にあるすべてのGameDebugMenuWidgetを取得する */ UFUNCTION(BlueprintCallable, Category = "GDM") TArray GetViewportDebugMenuWidgets(); UFUNCTION(BlueprintCallable, Category = "GDM") void ChangeConsoleCommandSetAsset(UGDMConsoleCommandSetAsset* NewCommandSetAsset); UFUNCTION(BlueprintPure, Category = "GDM") UGDMConsoleCommandSetAsset* GetConsoleCommandSetAsset() const; protected: virtual void CallExecuteConsoleCommandDispatcher(const FString& Command); virtual void CallShowDispatcher(); virtual void CallHideDispatcher(); UFUNCTION() virtual void OnWidgetAdded(UWidget* AddWidget, ULocalPlayer* Player); UFUNCTION() virtual void OnWidgetRemoved(UWidget* RemoveWidget); public: virtual void CallExecuteProcessEventDispatcher(const FName& FunctionName, UObject* TargetObject); virtual void CallChangePropertyBoolDispatcher(const FName& PropertyName, UObject* PropertyOwnerObject, bool New, bool Old, const FString& PropertySaveKey); virtual void CallChangePropertyIntDispatcher(const FName& PropertyName, UObject* PropertyOwnerObject, int32 New, int32 Old, const FString& PropertySaveKey); virtual void CallChangePropertyFloatDispatcher(const FName& PropertyName, UObject* PropertyOwnerObject, float New, float Old, const FString& PropertySaveKey); virtual void CallChangePropertyByteDispatcher(const FName& PropertyName, UObject* PropertyOwnerObject, uint8 New, uint8 Old, const FString& PropertySaveKey); virtual void CallChangePropertyStringDispatcher(const FName& PropertyName, UObject* PropertyOwnerObject, FString New, FString Old, const FString& PropertySaveKey); virtual void CallChangePropertyVectorDispatcher(const FName& PropertyName, UObject* PropertyOwnerObject, FVector New, FVector Old, const FString& PropertySaveKey); virtual void CallChangePropertyVector2DDispatcher(const FName& PropertyName, UObject* PropertyOwnerObject, FVector2D New, FVector2D Old, const FString& PropertySaveKey); virtual void CallChangePropertyRotatorDispatcher(const FName& PropertyName, UObject* PropertyOwnerObject, FRotator New, FRotator Old, const FString& PropertySaveKey); virtual void CallChangeDebugMenuLanguageDispatcher(const FName& NewLanguageKey, const FName& OldLanguageKey); virtual void CallStartScreenshotRequestDispatcher(); virtual void CallScreenshotRequestProcessedDispatcher(); virtual void CallLoadedDebugMenuDispatcher(); virtual void CallSavedDebugMenuDispatcher(); virtual void CallDeletedDebugMenuDispatcher(); }; ``` `Plugin/GameDebugMenu/Source/GameDebugMenu/Public/GameDebugMenuSettings.h`: ```h /** * Copyright (c) 2020 akihiko moroi * * This software is released under the MIT License. * (See accompanying file LICENSE.txt or copy at http://opensource.org/licenses/MIT) */ #pragma once #include "CoreMinimal.h" #include "Engine/DeveloperSettings.h" #include "Templates/SubclassOf.h" #include "GameDebugMenuTypes.h" #include "GameDebugMenuSettings.generated.h" class UGameDebugMenuMasterAsset; class AGameDebugMenuManager; class IGDMDeveloperSettingProvider; class AGDMDebugReportRequester; class UGDMEnhancedInputComponent; /** * DebugMenu用設定クラス */ UCLASS(config=GameDebugMenu, DefaultConfig) class GAMEDEBUGMENU_API UGameDebugMenuSettings : public UDeveloperSettings { GENERATED_BODY() public: /** マスターアセットのアセット名 */ UPROPERTY(EditAnywhere, config, Category = "Meta") FString MasterAssetName; /** コンソールコマンド名 */ UPROPERTY(EditAnywhere, config, Category = "ConsoleCommand") TArray ConsoleCommandNames; /** コンソールコマンドグループ名(同時に複数コマンド実行) */ UPROPERTY(EditAnywhere, config, Category = "ConsoleCommand") TArray ConsoleCommandGroups; /** コンソールコマンドペア名(トグルで2種のコマンド実行) */ UPROPERTY(EditAnywhere, config, Category = "ConsoleCommand") TArray ConsoleCommandPairs; /** コンソールコマンドナンバー(数値設定コマンド実行) */ UPROPERTY(EditAnywhere, config, Category = "ConsoleCommand") TArray ConsoleCommandNumbers; /** エディターでのみ追加されるコンソールコマンド名 */ UPROPERTY(EditAnywhere, config, Category = "ConsoleCommand") TArray EditorOnlyConsoleCommandNames; /** エディターでのみ追加されるコンソールコマンドグループ名 */ UPROPERTY(EditAnywhere, config, Category = "ConsoleCommand") TArray EditorOnlyConsoleCommandGroups; /** エディターでのみ追加されるコンソールコマンドペア名 */ UPROPERTY(EditAnywhere, config, Category = "ConsoleCommand") TArray EditorOnlyConsoleCommandPairs; /** エディターでのみ追加されるコンソールコマンドペア名 */ UPROPERTY(EditAnywhere, config, Category = "ConsoleCommand") TArray EditorOnlyConsoleCommandNumbers; /** コンソールコマンドのカテゴリ名表示順(最大255) */ UPROPERTY(EditAnywhere, config, Category = "ConsoleCommand") TArray OrderConsoleCommandCategoryTitles; /** Gameplayメニューのカテゴリ名表示順(最大255) */ UPROPERTY(EditAnywhere, config, Category = "Gameplay") TArray OrderGameplayCategoryTitles; /** DebugMenuのWidgetの入力優先度 */ UPROPERTY(EditAnywhere, config, Category = "Input") int32 WidgetInputActionPriority; /** バグレポート連携ツール */ UPROPERTY(EditAnywhere, config, Category = "ReportSettings") EGDMProjectManagementTool ProjectManagementToolType; /** Redmine連携用設定情報 */ UPROPERTY(EditAnywhere, config, Category = "ReportSettings") FGDMRedmineSettings RedmineSettings; /** Trello連携用設定情報 */ UPROPERTY(EditAnywhere, config, Category = "ReportSettings") FGDMTrelloSettings TrelloSettings; /** Jira連携用設定情報 */ UPROPERTY(EditAnywhere, config, Category = "ReportSettings") FGDMJiraSettings JiraSettings; /** バグレポート用の画面キャプチャ処理を無効化する */ UPROPERTY(EditAnywhere, Category = "ReportSettings") bool bDisableScreenCaptureProcessingWhenOpeningDebugMenu; /** DebugMenuから指定できるCultureのリスト */ UPROPERTY(EditAnywhere, config, Category = "Localization") TArray CultureList; /** デバックメニューの使用言語 */ UPROPERTY(EditAnywhere, config, Category = "Localization") FName DefaultGameDebugMenuLanguage; /** true: SaveGameを使用する。 false: Jsonファイルを使用する */ UPROPERTY(config, EditAnywhere, Category="Save") bool bUseSaveGame; /** JSON保存先のファイルパス(相対パス) */ UPROPERTY(config, EditAnywhere, Category="Save") FString SaveFilePath; /** JSON保存先のファイル名 */ UPROPERTY(config, EditAnywhere, Category="Save") FString SaveFileName; /** True: DebugMenuの保存機能を無効にする */ UPROPERTY(config, EditAnywhere, Category="Save") bool bDisableSaveFile; /** True: コンソールコマンドは保存されない False: 実行したコンソールコマンドはMaxCommandHistoryNumまで保存する(超えたら古いものから上書き) */ UPROPERTY(config, EditAnywhere, Category="Save") bool bDoesNotSaveConsoleCommand; /** 保存するコンソールコマンドの履歴件数 */ UPROPERTY(config, EditAnywhere, Category="Save") int32 MaxCommandHistoryNum; /** ここに含まれる文字のコンソールコマンドは保存されない */ UPROPERTY(config, EditAnywhere, Category="Save") TArray NoSaveConsoleCommands; /** DebugMenuでの改行文字 */ UPROPERTY(EditAnywhere, config, Category = "Other") FString LineBreakString; private: UPROPERTY(transient) mutable TObjectPtr MasterAsset; public: UGameDebugMenuSettings(); #if WITH_EDITOR virtual void PostEditChangeProperty(struct FPropertyChangedEvent& PropertyChangedEvent) override; virtual FText GetSectionText() const override; #endif TArray GetIssueCategoryNameList() const; TArray GetPriorityNameList() const; TArray GetAssigneeNameList() const; int32 GetDefaultIssueCategoryIndex() const; int32 GetDefaultPriorityIndex() const; FString GetGameplayCategoryTitle(const int32& ArrayIndex) const; int32 GetGameplayCategoryIndex(const int32& ArrayIndex) const; FString GetFullSavePath() const; const FGDMStringTableList* TryGetStringTableList(const FName& LanguageKey) const; TArray GetDebugMenuLanguageKeys() const; UClass* GetDebugMenuInputComponentClass() const; const TSubclassOf* GetDebugReportRequesterClass() const; FString GetDebugMenuString(const FName& LanguageKey, const FString& StringKey) const; UObject* GetDebugMenuFont() const; UGameDebugMenuMasterAsset* GetMasterAsset() const; private: void SetupCategoryResets(); void SetupCategorySlomo(); void SetupCategoryCamera(); void SetupCategoryProfiler(); void SetupCategoryDisplay(); void SetupCategoryShowDebug(); void SetupCategoryViewMode(); void SetupCategoryScalability(); void SetupCategoryFreeze(); void SetupCategoryDumpLogs(); void SetupCategoryNetwork(); void SetupCategorySound(); void SetupCategoryAbilitySystem(); void SetupCategoryOther(); void SetupCategoryLogVerbosity(); }; ``` `Plugin/GameDebugMenu/Source/GameDebugMenu/Public/GameDebugMenuTypes.h`: ```h /** * Copyright (c) 2020 akihiko moroi * * This software is released under the MIT License. * (See accompanying file LICENSE.txt or copy at http://opensource.org/licenses/MIT) */ #pragma once #include "CoreMinimal.h" #include "InputCoreTypes.h" #include "InputMappingContext.h" #include "Framework/Application/NavigationConfig.h" #include #include "GameDebugMenuTypes.generated.h" DECLARE_LOG_CATEGORY_EXTERN(LogGDM, Log, All); /** NavigationをきるためだけのConfig */ class FGDMNavigationConfig : public FNavigationConfig { public: FGDMNavigationConfig() :FNavigationConfig() { /** DebugMenuでは使用しない */ AnalogHorizontalKey = EKeys::Invalid; AnalogVerticalKey = EKeys::Invalid; KeyEventRules.Empty(); } virtual ~FGDMNavigationConfig() {} }; /** * */ UENUM(BlueprintType) enum class EGDMConsoleCommandType : uint8 { Non, Single, Group, Pair, Number, }; /** * メニュー選択後の動作 */ UENUM(BlueprintType) enum class EGDMConsoleCommandClickedEvent : uint8 { /** 特に何もしない */ Non, /** クリック後メニューを閉じる */ MenuClose, }; /** * プロパティの種類 */ UENUM(BlueprintType) enum class EGDMPropertyType : uint8 { GDM_Null, GDM_Bool, GDM_Int, GDM_Float, GDM_Enum, GDM_Byte, GDM_String, GDM_Vector, GDM_Vector2D, GDM_Rotator, }; /** * プロパティ編集時の最大値、最小値 */ USTRUCT(BlueprintType) struct GAMEDEBUGMENU_API FGDMPropertyRange { GENERATED_BODY() UPROPERTY(EditAnywhere, BlueprintReadWrite) bool bUseMin; UPROPERTY(EditAnywhere, BlueprintReadWrite) bool bUseMax; UPROPERTY(EditAnywhere, BlueprintReadWrite) float MinValue; UPROPERTY(EditAnywhere, BlueprintReadWrite) float MaxValue; FGDMPropertyRange() : bUseMin(false) , bUseMax(false) , MinValue(0.0f) , MaxValue(0.0f) { } }; /** * プロパティ編集時のUI設定情報 */ USTRUCT(BlueprintType) struct GAMEDEBUGMENU_API FGDMPropertyUIConfigInfo { GENERATED_BODY() /** 設定範囲 */ UPROPERTY(EditAnywhere, BlueprintReadWrite) FGDMPropertyRange Range; /** 値の変化量 */ UPROPERTY(EditAnywhere, BlueprintReadWrite) float DefaultChangeAmount; /** 最大変化量 */ UPROPERTY(EditAnywhere, BlueprintReadWrite) float MaxChangeAmount; /** 最大変化量になるまでの時間 */ UPROPERTY(EditAnywhere, BlueprintReadWrite) float MaxChangeAmountTime; FGDMPropertyUIConfigInfo() : Range() , DefaultChangeAmount(1.0f) , MaxChangeAmount(1.0f) , MaxChangeAmountTime(1.3f) { } }; /** * コマンドでの実行範囲 */ UENUM(BlueprintType) enum class EGDMConsoleCommandNetType : uint8 { /** ローカルでのみ実行する */ LocalOnly, /** Serverで全プレイヤーに実行させる */ ServerAll, }; /** * UIでコンソールコマンドを操作するのに使用する情報 */ USTRUCT(BlueprintType) struct GAMEDEBUGMENU_API FGDMConsoleCommand { GENERATED_BODY() UPROPERTY(BlueprintReadWrite) EGDMConsoleCommandType Type; UPROPERTY(EditAnywhere,BlueprintReadWrite,config) EGDMConsoleCommandClickedEvent ClickedEvent; UPROPERTY(EditAnywhere, BlueprintReadWrite, config) uint8 CategoryIndex; UPROPERTY(EditAnywhere, BlueprintReadWrite, config) FText Title; UPROPERTY(EditAnywhere, BlueprintReadWrite, config) FText Description; UPROPERTY(EditAnywhere, BlueprintReadWrite, config) EGDMConsoleCommandNetType CommandNetType; FGDMConsoleCommand(); virtual ~FGDMConsoleCommand() = default; virtual FString BuildCommandIdentifier() const PURE_VIRTUAL(FGDMConsoleCommand::GetCommandId, return FString();); }; /** * UIで実行するときに単体で実行する */ USTRUCT(BlueprintType) struct GAMEDEBUGMENU_API FGDMConsoleCommandSingle : public FGDMConsoleCommand { GENERATED_BODY() UPROPERTY(EditAnywhere, BlueprintReadWrite, config) FString ConsoleCommandName; FGDMConsoleCommandSingle(); virtual FString BuildCommandIdentifier() const override; }; /** * UIで実行するときに同時に実行できるようにするためのグループ */ USTRUCT(BlueprintType) struct GAMEDEBUGMENU_API FGDMConsoleCommandGroup : public FGDMConsoleCommand { GENERATED_BODY() UPROPERTY(EditAnywhere, BlueprintReadWrite, config) TArray ConsoleCommandNames; FGDMConsoleCommandGroup(); virtual FString BuildCommandIdentifier() const override; }; /** * UIでトグル操作するときのペアになるコマンド */ USTRUCT(BlueprintType) struct GAMEDEBUGMENU_API FGDMConsoleCommandPair : public FGDMConsoleCommand { GENERATED_BODY() UPROPERTY(EditAnywhere, BlueprintReadWrite, config) FString FirstConsoleCommandName; UPROPERTY(EditAnywhere, BlueprintReadWrite, config) FString SecondConsoleCommandName; FGDMConsoleCommandPair(); virtual FString BuildCommandIdentifier() const override; }; /** * UIで実行するときに数字指定で実行する */ USTRUCT(BlueprintType) struct GAMEDEBUGMENU_API FGDMConsoleCommandNumber : public FGDMConsoleCommand { GENERATED_BODY() UPROPERTY(EditAnywhere, BlueprintReadWrite, config) FString ConsoleCommandName; UPROPERTY(EditAnywhere, BlueprintReadWrite, config) FString PreConsoleCommandName; UPROPERTY(EditAnywhere, BlueprintReadWrite, config) FString PostConsoleCommandName; UPROPERTY(EditAnywhere, BlueprintReadWrite, config) FGDMPropertyUIConfigInfo UIConfigInfo; UPROPERTY(EditAnywhere, BlueprintReadWrite, config) float DefaultValue; /** コマンドの値を取得するときに使用するコンソール名 */ UPROPERTY(EditAnywhere, BlueprintReadWrite, config) FString ConsoleVariableName; FGDMConsoleCommandNumber(); virtual FString BuildCommandIdentifier() const override; }; /** * */ USTRUCT(BlueprintType) struct GAMEDEBUGMENU_API FGDMMenuCategoryKey { GENERATED_USTRUCT_BODY() UPROPERTY(EditAnywhere, BlueprintReadWrite) uint8 Index; UPROPERTY(EditAnywhere, BlueprintReadWrite) FString KeyName; public: FGDMMenuCategoryKey(uint8 InIndex, FString InKeyName); FGDMMenuCategoryKey(uint8 InIndex); FGDMMenuCategoryKey(); bool operator==(FGDMMenuCategoryKey& InOther); bool operator!=(FGDMMenuCategoryKey& InOther); bool operator<(FGDMMenuCategoryKey& InOther); bool operator>(FGDMMenuCategoryKey& InOther); FORCEINLINE operator uint8() const { return Index; } }; USTRUCT(BlueprintType) struct GAMEDEBUGMENU_API FGDMGameplayCategoryKey : public FGDMMenuCategoryKey { GENERATED_USTRUCT_BODY() public: FGDMGameplayCategoryKey(uint8 InIndex, FString InKeyName); FGDMGameplayCategoryKey(uint8 InIndex); FGDMGameplayCategoryKey(); }; /** * Menuに登録できるプロパティ情報 */ class GAMEDEBUGMENU_API FGDMObjectPropertyInfo { public: FGDMGameplayCategoryKey CategoryKey; FText Name; FText Description; TWeakObjectPtr TargetObject; FProperty* TargetProperty; FName PropertyName; TWeakObjectPtr EnumType; FGDMPropertyUIConfigInfo ConfigInfo; int32 DisplayPriority; UScriptStruct* Struct; FString PropertySaveKey; FGDMObjectPropertyInfo() : CategoryKey() , Name(FText::GetEmpty()) , Description(FText::GetEmpty()) , TargetObject(nullptr) , TargetProperty(nullptr) , PropertyName(NAME_None) , EnumType(nullptr) , ConfigInfo() , DisplayPriority(0) , Struct(nullptr) , PropertySaveKey() { } }; /** * Menuに登録できる関数情報 */ class GAMEDEBUGMENU_API FGDMObjectFunctionInfo { public: FGDMGameplayCategoryKey CategoryKey; FText Name; FText Description; TWeakObjectPtr TargetObject; TWeakObjectPtr TargetFunction; FName FunctionName; int32 DisplayPriority; FString FunctionSaveKey; FGDMObjectFunctionInfo() : CategoryKey() , Name(FText::GetEmpty()) , Description(FText::GetEmpty()) , TargetObject(nullptr) , TargetFunction(nullptr) , FunctionName(NAME_None) , DisplayPriority(0) , FunctionSaveKey() { } }; /** * 未登録状態のオブジェクト情報 */ struct GAMEDEBUGMENU_API FGDMPendingObjectData { TWeakObjectPtr TargetObject; FName TargetName; FGDMGameplayCategoryKey CategoryKey; FText DisplayPropertyName; FText Description; FGDMPropertyUIConfigInfo ConfigInfo; int32 DisplayPriority; FString SaveKey; FGDMPendingObjectData() : TargetObject(nullptr) , TargetName(NAME_None) , CategoryKey() , DisplayPropertyName(FText::GetEmpty()) , Description(FText::GetEmpty()) , ConfigInfo() , DisplayPriority(0) , SaveKey() { } }; /** * */ UENUM(BlueprintType) enum class EGDMProjectManagementTool : uint8 { Redmine, Trello, Jira, }; USTRUCT(BlueprintType) struct GAMEDEBUGMENU_API FGDMProjectManagementToolSettings { GENERATED_BODY() /** 生成したAPIキー */ UPROPERTY(EditAnywhere, BlueprintReadWrite, config) FString AccessKey; FGDMProjectManagementToolSettings() :AccessKey() { } }; /** * Redmine連携用の情報 */ USTRUCT(BlueprintType) struct GAMEDEBUGMENU_API FGDMRedmineSettings : public FGDMProjectManagementToolSettings { GENERATED_BODY() /** 接続先ホスト名 */ UPROPERTY(EditAnywhere,BlueprintReadWrite,config) FString HostName; /** プロジェクト識別ID */ UPROPERTY(EditAnywhere, BlueprintReadWrite, config, meta = (UIMin = 1, ClampMin = 1)) int32 ProjectId; /** トラッカー名 */ UPROPERTY(EditAnywhere, BlueprintReadWrite, config) TArray TrackerNameList; /** トラッカー初期値 */ UPROPERTY(EditAnywhere, BlueprintReadWrite, config, meta = (UIMin = 1, ClampMin = 1)) int32 DefaultTrackerIndex; /** 優先度名 */ UPROPERTY(EditAnywhere, BlueprintReadWrite, config) TArray PriorityNameList; /** 優先度初期値 */ UPROPERTY(EditAnywhere, BlueprintReadWrite, config, meta = (UIMin = 1, ClampMin = 1)) int32 DefaultPriorityIndex; FGDMRedmineSettings() : Super() , HostName(TEXT("localhost")) , ProjectId(1) , TrackerNameList() , DefaultTrackerIndex(0) , PriorityNameList() , DefaultPriorityIndex(1) { TrackerNameList.Reset(); TrackerNameList.Add(FText::FromString(TEXT("バグ"))); TrackerNameList.Add(FText::FromString(TEXT("機能"))); TrackerNameList.Add(FText::FromString(TEXT("サポート"))); PriorityNameList.Reset(); PriorityNameList.Add(FText::FromString(TEXT("低め"))); PriorityNameList.Add(FText::FromString(TEXT("通常"))); PriorityNameList.Add(FText::FromString(TEXT("高め"))); PriorityNameList.Add(FText::FromString(TEXT("急いで"))); PriorityNameList.Add(FText::FromString(TEXT("今すぐ"))); } }; /** * Trello連携用の情報 */ USTRUCT(BlueprintType) struct GAMEDEBUGMENU_API FGDMTrelloSettings : public FGDMProjectManagementToolSettings { GENERATED_BODY() /** Trello用に生成したトークン */ UPROPERTY(EditAnywhere, BlueprintReadWrite, config) FString AccessToken; /** 追加先リストID */ UPROPERTY(EditAnywhere, BlueprintReadWrite, config) TArray CardListIDs; /** 追加先リスト名 */ UPROPERTY(EditAnywhere, BlueprintReadWrite, config) TArray CardListNames; FGDMTrelloSettings() : Super() , AccessToken() , CardListIDs() , CardListNames() { CardListNames.Add(FText::FromString(TEXT("Trello追加先リスト名"))); } }; /** * Jira連携用の情報 */ USTRUCT(BlueprintType) struct GAMEDEBUGMENU_API FGDMJiraSettings : public FGDMProjectManagementToolSettings { GENERATED_BODY() /** 接続先ホスト名 */ UPROPERTY(EditAnywhere, BlueprintReadWrite, config) FString HostName; /** プロジェクト識別KEY名 */ UPROPERTY(EditAnywhere, BlueprintReadWrite, config) FString ProjectKeyName; /** ユーザー名 */ UPROPERTY(EditAnywhere, BlueprintReadWrite, config) FString UserName; /** 課題の種類 */ UPROPERTY(EditAnywhere, BlueprintReadWrite, config) TArray IssueTypeList; /** 課題の種類初期値 */ UPROPERTY(EditAnywhere, BlueprintReadWrite, config) int32 DefaultIssueTypeIndex; /** 優先度名 */ UPROPERTY(EditAnywhere, BlueprintReadWrite, config) TArray PriorityNameList; /** 優先度初期値 */ UPROPERTY(EditAnywhere, BlueprintReadWrite, config) int32 DefaultPriorityIndex; /** 担当者名リスト(Key: accountId Value: 名前) */ UPROPERTY(EditAnywhere, BlueprintReadWrite, config) TMap AssigneeList; UPROPERTY(EditAnywhere,BlueprintReadWrite,config) TArray LabelNameList; FGDMJiraSettings() : Super() , HostName(TEXT("localhost")) , ProjectKeyName() , UserName() , IssueTypeList() , DefaultIssueTypeIndex(1) , PriorityNameList() , DefaultPriorityIndex(2) , AssigneeList() , LabelNameList() { IssueTypeList.Reset(); IssueTypeList.Add(FText::FromString(TEXT("タスク"))); IssueTypeList.Add(FText::FromString(TEXT("バグ"))); IssueTypeList.Add(FText::FromString(TEXT("改善"))); PriorityNameList.Reset(); PriorityNameList.Add(FText::FromString(TEXT("Highest"))); PriorityNameList.Add(FText::FromString(TEXT("High"))); PriorityNameList.Add(FText::FromString(TEXT("Medium"))); PriorityNameList.Add(FText::FromString(TEXT("Low"))); PriorityNameList.Add(FText::FromString(TEXT("Lowest"))); } FString GetAssigneeAccountIdByListIndex(int32 ListIndex) const; FText GetAssigneeTextByListIndex(int32 ListIndex) const; }; /** * DebugMenuの文字用のStringTableのリスト */ USTRUCT(BlueprintType) struct GAMEDEBUGMENU_API FGDMStringTableList { GENERATED_BODY() UPROPERTY(EditAnywhere, BlueprintReadWrite) TArray> StringTables; FGDMStringTableList() :StringTables() { } }; /** * リスト表示メニューのカテゴリ名 */ USTRUCT(BlueprintType) struct GAMEDEBUGMENU_API FGDMOrderMenuCategoryTitle { GENERATED_BODY() UPROPERTY(EditAnywhere) FString Title; UPROPERTY(VisibleAnywhere) int32 Index; #if WITH_EDITORONLY_DATA UPROPERTY(VisibleAnywhere) FString PreviewTitle; #endif FGDMOrderMenuCategoryTitle() : Title() , Index(INDEX_NONE) #if WITH_EDITORONLY_DATA , PreviewTitle() #endif { } FGDMOrderMenuCategoryTitle(FString InTitle) : Title(InTitle) , Index(INDEX_NONE) #if WITH_EDITORONLY_DATA , PreviewTitle() #endif { } FGDMOrderMenuCategoryTitle(FString InTitle, int32 InIndex) : Title(InTitle) , Index(InIndex) #if WITH_EDITORONLY_DATA , PreviewTitle() #endif { } FGDMOrderMenuCategoryTitle(FString InTitle, FString InPreviewTitle) : Title(InTitle) , Index(INDEX_NONE) #if WITH_EDITORONLY_DATA , PreviewTitle(InPreviewTitle) #endif { } }; /** * */ USTRUCT(BlueprintType) struct FGameDebugMenuWidgetInputMappingContextData { GENERATED_BODY() UPROPERTY(Editanywhere, BlueprintReadWrite) TObjectPtr InputMappingContext = nullptr; UPROPERTY(Editanywhere, BlueprintReadWrite) int32 Priority = 1000000; }; /** * お気に入り項目の登録情報 */ USTRUCT(BlueprintType) struct FGDMFavoriteEntry { GENERATED_BODY() UPROPERTY(EditAnywhere, BlueprintReadWrite) FString DefinitionName; UPROPERTY(EditAnywhere, BlueprintReadWrite) FString SaveKey; }; ``` `Plugin/GameDebugMenu/Source/GameDebugMenu/Public/Input/GDMDebugCameraInput.h`: ```h /** * Copyright (c) 2020 akihiko moroi * * This software is released under the MIT License. * (See accompanying file LICENSE.txt or copy at http://opensource.org/licenses/MIT) */ #pragma once #include "CoreMinimal.h" #include "GameFramework/Actor.h" #include "GDMDebugCameraInput.generated.h" class ADebugCameraController; class AGameDebugMenuManager; /** * DebugCameraController動作中に処理される入力判定を制御するクラス * 「ToogleDebugCamera」実行時に別途、入力機能(メニューの開閉など)をここで実装できるようにしてる * (BP_GDMDebugCameraInput参照) */ UCLASS(Blueprintable, NotBlueprintType, notplaceable) class GAMEDEBUGMENU_API AGDMDebugCameraInput : public AActor { GENERATED_BODY() FDelegateHandle ActorSpawnedDelegateHandle; TWeakObjectPtr DebugCameraController; public: AGDMDebugCameraInput(); public: UFUNCTION(BlueprintPure) AGameDebugMenuManager* GetOwnerGameDebugMenuManager() const; UFUNCTION(BlueprintPure) ADebugCameraController* GetDebugCameraController() const; void SetDebugCameraController(ADebugCameraController* DCC); UFUNCTION(BlueprintCallable) virtual void ToggleOrbitHitPoint(); UFUNCTION(BlueprintCallable) virtual void PawnTeleport(); }; ``` `Plugin/GameDebugMenu/Source/GameDebugMenu/Public/Input/GDMEnhancedInputComponent.h`: ```h /** * Copyright (c) 2020 akihiko moroi * * This software is released under the MIT License. * (See accompanying file LICENSE.txt or copy at http://opensource.org/licenses/MIT) */ #pragma once #include "CoreMinimal.h" #include "EnhancedInputComponent.h" #include "GDMEnhancedInputComponent.generated.h" /** * DebugMenu用のUIで入力判定を制御する専用のEnhancedInputComponent */ UCLASS() class GAMEDEBUGMENU_API UGDMEnhancedInputComponent : public UEnhancedInputComponent { GENERATED_BODY() public: UGDMEnhancedInputComponent(); virtual bool CanProcessInputAction(const UInputAction* Action) const; FEnhancedInputActionEventBinding& BindDebugMenuAction(const UInputAction* Action, ETriggerEvent TriggerEvent, UObject* Object, FName FunctionName); FEnhancedInputActionEventBinding& BindDebugMenuAction(const UInputAction* Action, ETriggerEvent TriggerEvent, TFunction&& Callback); }; ``` `Plugin/GameDebugMenu/Source/GameDebugMenu/Public/Input/GDMInputSystemComponent.h`: ```h /** * Copyright (c) 2020 akihiko moroi * * This software is released under the MIT License. * (See accompanying file LICENSE.txt or copy at http://opensource.org/licenses/MIT) */ #pragma once #include "CoreMinimal.h" #include "Data/GameDebugMenuManagerAsset.h" #include "GDMInputSystemComponent.generated.h" class ADebugCameraController; class UGDMEnhancedInputComponent; class AGameDebugMenuManager; class AGDMDebugCameraInput; /** * DebugMenuでの入力処理を管理するコンポーネント */ UCLASS() class GAMEDEBUGMENU_API UGDMInputSystemComponent : public UActorComponent { GENERATED_BODY() protected: /** DebugMenuの入力を無視するフラグカウント */ uint16 IgnoreDebugMenuInput; /** デバックカメラ用のインプットアクターのインスタンス */ UPROPERTY(Transient) TObjectPtr DebugCameraInput; /** 登録中のInputComponent郡 */ TMap>> RegisteredInputGroups; /** 利用中のInputComponent郡 */ TMap>> ActiveInputStacks; /** 現在アクティブなInputComponentのグループ名 */ FName CurrentInputGroupName; /** メニューの開閉状態の識別フラグ */ UPROPERTY(Transient) bool bMenuOpen; /** ルートのWidgetが持つInputComponent */ UPROPERTY(Transient) TObjectPtr RootWidgetInputComponent; /** DebugMenuManagerが作られたときに追加される InputMappingContext */ UPROPERTY(Transient) TArray AddInputMappingContextWhenCreateManager; /** DebugMenuを表示するときに追加される InputMappingContext */ UPROPERTY(Transient) TArray AddInputMappingContextWhenDebugMenuIsShow; /** ADebugCameraController生成チェック用のハンドル */ FDelegateHandle ActorSpawnedDelegateHandle; TWeakObjectPtr DebugCameraController; public: /** 入力判定用ログ */ UPROPERTY(BlueprintReadWrite, Category = "GDM|Debug") bool bOutputDebugLog; public: UGDMInputSystemComponent(); virtual void TickComponent(float DeltaTime, enum ELevelTick TickType, FActorComponentTickFunction* ThisTickFunction) override; virtual void EndPlay(const EEndPlayReason::Type EndPlayReason) override; public: virtual void SetIgnoreInput(bool bNewInput); virtual void ResetIgnoreInput(); virtual bool IsInputIgnored() const; /** * 初期化 */ virtual void Initialize(UGameDebugMenuManagerAsset* MenuDataAsset); /** * 現在アクティブなグループにInputComponentを登録/解除をする */ virtual void RegisterInputComponent(UInputComponent* InputComponent); virtual void UnregisterInputComponent(UInputComponent* InputComponent); /** * 指定したグループにInputComponentを登録/解除をする */ virtual void RegisterInputComponentToGroup(UInputComponent* InputComponent, const FName GroupName); virtual void UnregisterInputComponentFromGroup(UInputComponent* InputComponent, const FName GroupName); /** * 入力グループを変更する * @param NewGroupName - 新しいグループ名 */ virtual void SwitchToInputGroup(const FName NewGroupName); /** * デバックメニューが開くと呼ばれる * @note アクティブなメニューのInputComponentをPlayerControllerに追加する */ virtual void OnOpenMenu(); /** * デバックメニューが閉じると呼ばれる * @note アクティブなメニューのInputComponentをPlayerControllerに削除する */ virtual void OnCloseMenu(); protected: virtual void CreateDebugCameraInputClass(TSubclassOf DebugCameraInputClass); AGameDebugMenuManager* GetOwnerGameDebugMenuManager() const; TArray GetPlayerControllers() const; virtual void OnActorSpawned(AActor* SpawnActor); }; ``` `Plugin/GameDebugMenu/Source/GameDebugMenu/Public/Input/GDMInputTriggerPulseWithDelay.h`: ```h /** * Copyright (c) 2020 akihiko moroi * * This software is released under the MIT License. * (See accompanying file LICENSE.txt or copy at http://opensource.org/licenses/MIT) */ #pragma once #include "CoreMinimal.h" #include "InputTriggers.h" #include "GDMInputTriggerPulseWithDelay.generated.h" /** * 最初と2回目以降のトリガー時間を別に指定できるInputTrigger */ UCLASS(NotBlueprintable, meta = (DisplayName = "GDMPulseWithDelay")) class GAMEDEBUGMENU_API UGDMInputTriggerPulseWithDelay : public UInputTrigger/* UInputTriggerTimedBaseのENHANCEDINPUT_APIがなぜかUE5.6で削除された.... */ { GENERATED_BODY() public: /** 押した瞬間にも発火させるか */ UPROPERTY(EditAnywhere, Config, BlueprintReadWrite, Category = "Trigger Settings") bool bTriggerOnStart = true; /** 最初の1回目を発火するまでの遅延(秒) */ UPROPERTY(EditAnywhere, Config, BlueprintReadWrite, Category = "Trigger Settings", meta = (ClampMin = "0")) float InitialDelay = 0.5f; /** 2回目以降のトリガー間隔(秒) */ UPROPERTY(EditAnywhere, Config, BlueprintReadWrite, Category = "Trigger Settings", meta = (ClampMin = "0")) float RepeatInterval = 0.1f; /** 入力が保持されている間の最大トリガー回数(0 = 無制限) */ UPROPERTY(EditAnywhere, Config, BlueprintReadWrite, Category = "Trigger Settings", meta = (ClampMin = "0")) int32 TriggerLimit = 0; // How long have we been actuating this trigger? UPROPERTY(BlueprintReadWrite, Category = "Trigger Settings") float HeldDuration = 0.0f; /** * Should global time dilation be applied to the held duration? * Default is set to false. * * If this is set to true, then the owning Player Controller's actor time dilation * will be used when calculating the HeldDuration. * * @see UInputTriggerTimedBase::CalculateHeldDuration * @see AWorldSettings::GetEffectiveTimeDilation */ UPROPERTY(EditAnywhere, Config, BlueprintReadWrite, Category = "Trigger Settings") bool bAffectedByTimeDilation = false; private: bool bWasPressed = false; bool bTriggeredOnStart = false; int32 TriggerCount = 0; public: virtual FString GetDebugState() const override; virtual ETriggerEventsSupported GetSupportedTriggerEvents() const override { return ETriggerEventsSupported::Ongoing; } virtual ETriggerState UpdateState_Implementation(const UEnhancedPlayerInput* PlayerInput, FInputActionValue ModifiedValue, float DeltaTime) override; float CalculateHeldDuration(const UEnhancedPlayerInput* PlayerInput, float DeltaTime) const; }; ``` `Plugin/GameDebugMenu/Source/GameDebugMenu/Public/Input/GDMPadInputWidgetController.h`: ```h /** * Copyright (c) 2020 akihiko moroi * * This software is released under the MIT License. * (See accompanying file LICENSE.txt or copy at http://opensource.org/licenses/MIT) */ #pragma once #include "CoreMinimal.h" #include "Components/Widget.h" #include "GDMPadInputWidgetController.generated.h" class UGDMListenerComponent; class UGameDebugMenuWidget; /** * DebugMenuの中で、パッド操作などによって「選択状態」の Widget を制御するためのクラス * 主にリスト表記のUIでの選択位置や見た目の更新を行うためのインターフェースを提供します * 詳細はBP_GDM_PadInputWidgetController,WB_GDM_ReportMenu,WB_GDM_FavoriteMenu参照 */ UCLASS(Blueprintable) class GAMEDEBUGMENU_API UGDMPadInputWidgetController : public UObject { GENERATED_BODY() protected: UPROPERTY(BlueprintReadOnly, meta = (ExposeOnSpawn)) UGameDebugMenuWidget* OwnerGameDebugMenuWidget; public: virtual UWorld* GetWorld() const override; /** * 次のWidgetを「選択中」にする * @return 現在の選択中インデックスを返す */ UFUNCTION(BlueprintCallable, BlueprintImplementableEvent, Category = "GDM") int32 NextChoosing(); /** * 1つ前のWidgetを「選択中」にする * @return 現在の選択中インデックスを返す */ UFUNCTION(BlueprintCallable, BlueprintImplementableEvent, Category = "GDM") int32 PreviousChoosing(); /** * 現在「選択中」のWidgetを取得する */ UFUNCTION(BlueprintCallable, BlueprintImplementableEvent, Category = "GDM") UWidget* GetChoosingWidget() const; /** * 現在「選択中」のWidgetのインデックスを取得する */ UFUNCTION(BlueprintCallable, BlueprintImplementableEvent, Category = "GDM") int32 GetChoosingWidgetIndex() const; /** * 現在「選択した」のWidgetを取得する */ UFUNCTION(BlueprintCallable, BlueprintImplementableEvent, Category = "GDM") UWidget* GetChosenWidget() const; /** * 現在「選択した」のWidgetのインデックスを取得する */ UFUNCTION(BlueprintCallable, BlueprintImplementableEvent, Category = "GDM") int32 GetChosenWidgetIndex() const; /** * 選択が確定された状態かどうかを返す * @return true: 選択した状態 false: 選択中 */ UFUNCTION(BlueprintCallable, BlueprintImplementableEvent, Category = "GDM") bool IsChosen() const; /** * 選択状態を「選択確定(Chosen)」に切り替える */ UFUNCTION(BlueprintCallable, BlueprintImplementableEvent, Category = "GDM") void ChangeChosenMode(); /** * 選択状態を「選択中(Choosing)」に切り替える */ UFUNCTION(BlueprintCallable, BlueprintImplementableEvent, Category = "GDM") void ChangeChoosingMode(); /** * 新しく「選択中」とする Widget を直接指定 */ UFUNCTION(BlueprintCallable, BlueprintImplementableEvent, Category = "GDM") void SetChoosingWidget(UWidget* NewChoosingWidget); /** * 指定した UWidget が内部で管理していれていれば、そのインデックスを返す * @param TargetWidget - 対象のWidget * @return あれば 0 以上,なければ -1を返す */ UFUNCTION(BlueprintCallable, BlueprintPure=false, BlueprintImplementableEvent, Category = "GDM") int32 GetWidgetIndexByWidget(UWidget* TargetWidget) const; /** * 内部で管理している Widget 群の中から、インデックスに対応する Widget を取得 * @param Index - 対象のインデックス * @return 無効なインデックスなら nullptr を返す */ UFUNCTION(BlueprintCallable, BlueprintPure=false, BlueprintImplementableEvent, Category = "GDM") UWidget* GetWidgetByIndex(int32 Index) const; /** * 管理してるWidgetの数を取得 */ UFUNCTION(BlueprintCallable, BlueprintImplementableEvent, Category = "GDM") int32 GetWidgetCount() const; }; ``` `Plugin/GameDebugMenu/Source/GameDebugMenu/Public/Log/GDMOutputDevice.h`: ```h /** * Copyright (c) 2020 akihiko moroi * * This software is released under the MIT License. * (See accompanying file LICENSE.txt or copy at http://opensource.org/licenses/MIT) */ #pragma once #include "CoreMinimal.h" #include "UObject/NoExportTypes.h" #include "Misc/OutputDevice.h" #include class AGameDebugMenuManager; /** * DebugMenuで使用できるようにするOutputLogの文字情報 * プロジェクト名.log取得したかったけど起動中はアクセスできないため文字列情報はこれから取得。 * ただAGameDebugMenuManagerが生成されてから動作するので正確には同じではない */ class GAMEDEBUGMENU_API FGDMOutputDevice : public FOutputDevice { TArray Logs; TArray CommandHistory; mutable FCriticalSection CommandHistoryMutex; public: FGDMOutputDevice(); virtual ~FGDMOutputDevice() override; virtual void Serialize(const TCHAR* Data, ELogVerbosity::Type Verbosity, const class FName& Category, const double Time) override; virtual void Serialize(const TCHAR* Data, ELogVerbosity::Type Verbosity, const class FName& Category) override; public: TArray GetLogs() const; TArray GetCommandHistory() const; void ClearCommandHistory(); }; ``` `Plugin/GameDebugMenu/Source/GameDebugMenu/Public/Reports/GDMDebugReportRequester.h`: ```h /** * Copyright (c) 2020 akihiko moroi * * This software is released under the MIT License. * (See accompanying file LICENSE.txt or copy at http://opensource.org/licenses/MIT) */ #pragma once #include "CoreMinimal.h" #include "GameFramework/Actor.h" #include "WebImage.h" #include "GDMDebugReportRequester.generated.h" class AGameDebugMenuManager; /** * バグレポート送信処理を行うクラス */ UCLASS(notplaceable) class GAMEDEBUGMENU_API AGDMDebugReportRequester : public AActor { GENERATED_BODY() public: static const FString LineBreak; bool bSendLogs; bool bSendScreenshotCapture; FString Subject; FString Description; int32 IssueCategoryIndex; int32 PriorityIndex; int32 AssigneeIndex; TArray ScreenshotImageData; FDateTime ScreenshotCapturedDateTime; int32 TestCount; int32 MaxTestCount; bool bWasRequestSuccessful; public: AGDMDebugReportRequester(); public: virtual void StartRequest(); protected: virtual void SuccessRequest(); virtual void FailedRequest(); AGameDebugMenuManager* GetOwnerDebugMenuManager() const; FString GetSubject(); FString GetDescription(); UFUNCTION(BlueprintNativeEvent) FString PrefixSubjectString(); UFUNCTION(BlueprintNativeEvent) FString PrefixDescriptionString(); UFUNCTION(BlueprintNativeEvent) FString SuffixSubjectString(); UFUNCTION(BlueprintNativeEvent) FString SuffixDescriptionString(); /* multipart/form-data形式で送信する場合使用 */ int32 GetUTF8StringSize(const FString& Text); FString MakeBoundaryString(); void AddContentString(const FString& BoundaryKey, const FString& DataName, const FString& ContentType, const FString& SourceData, TArray& OutSendData); void AddContent(const FString& BoundaryKey, const FString& DataName, const FString& ContentType, const TArray& SourceData, TArray& OutSendData); void AddEndContentString(const FString& BoundaryKey, TArray& OutSendData); }; ``` `Plugin/GameDebugMenu/Source/GameDebugMenu/Public/Reports/GDMRequesterJira.h`: ```h /** * Copyright (c) 2020 akihiko moroi * * This software is released under the MIT License. * (See accompanying file LICENSE.txt or copy at http://opensource.org/licenses/MIT) */ #pragma once #include "CoreMinimal.h" #include "Reports/GDMDebugReportRequester.h" #include "GDMRequesterJira.generated.h" /** * Jiraへのバグレポート送信処理 */ UCLASS() class GAMEDEBUGMENU_API AGDMRequesterJira : public AGDMDebugReportRequester { GENERATED_BODY() public: virtual void StartRequest() override; protected: virtual void RequestUploadContent(const FString& IssueKey); virtual void OnResponseReceived(FHttpRequestPtr Request, FHttpResponsePtr Response, bool bWasSuccessful); virtual void OnResponseReceivedUploaded(FHttpRequestPtr Request, FHttpResponsePtr Response, bool bWasSuccessful); }; ``` `Plugin/GameDebugMenu/Source/GameDebugMenu/Public/Reports/GDMRequesterRedmine.h`: ```h /** * Copyright (c) 2020 akihiko moroi * * This software is released under the MIT License. * (See accompanying file LICENSE.txt or copy at http://opensource.org/licenses/MIT) */ #pragma once #include "CoreMinimal.h" #include "Reports/GDMDebugReportRequester.h" #include "GDMRequesterRedmine.generated.h" /** * Redmineへのバグレポート送信処理 */ UCLASS() class GAMEDEBUGMENU_API AGDMRequesterRedmine : public AGDMDebugReportRequester { GENERATED_BODY() FString TokenScreenshotCapture; FString TokenLog; public: virtual void StartRequest() override; protected: virtual void RequestUploadScreenshotCapture(); virtual void RequestUploadLog(); virtual void RequestIssues(); virtual void OnResponseReceived(FHttpRequestPtr Request, FHttpResponsePtr Response, bool bWasSuccessful); virtual void OnResponseReceivedUploaded(FHttpRequestPtr Request, FHttpResponsePtr Response, bool bWasSuccessful); virtual void OnResponseReceivedLog(FHttpRequestPtr Request, FHttpResponsePtr Response, bool bWasSuccessful); }; ``` `Plugin/GameDebugMenu/Source/GameDebugMenu/Public/Reports/GDMRequesterTrello.h`: ```h /** * Copyright (c) 2020 akihiko moroi * * This software is released under the MIT License. * (See accompanying file LICENSE.txt or copy at http://opensource.org/licenses/MIT) */ #pragma once #include "CoreMinimal.h" #include "Reports/GDMDebugReportRequester.h" #include "GDMRequesterTrello.generated.h" /** * Trelloへのバグレポート送信処理 */ UCLASS() class GAMEDEBUGMENU_API AGDMRequesterTrello : public AGDMDebugReportRequester { GENERATED_BODY() FString AttachmentCardListID; public: virtual void StartRequest() override; protected: virtual void RequestUploadScreenshotCapture(); virtual void RequestUploadLog(); virtual void OnResponseReceived(FHttpRequestPtr Request, FHttpResponsePtr Response, bool bWasSuccessful); virtual void OnResponseReceivedUploaded(FHttpRequestPtr Request, FHttpResponsePtr Response, bool bWasSuccessful); virtual void OnResponseReceivedLog(FHttpRequestPtr Request, FHttpResponsePtr Response, bool bWasSuccessful); }; ``` `Plugin/GameDebugMenu/Source/GameDebugMenu/Public/Widgets/GDMButton.h`: ```h /** * Copyright (c) 2020 akihiko moroi * * This software is released under the MIT License. * (See accompanying file LICENSE.txt or copy at http://opensource.org/licenses/MIT) */ #pragma once #include "CoreMinimal.h" #include "Components/Button.h" #include "GDMButton.generated.h" /** * GameDebugMenu用のボタン */ UCLASS() class GAMEDEBUGMENU_API UGDMButton : public UButton { GENERATED_BODY() public: UGDMButton(const FObjectInitializer& ObjectInitializer); public: #if WITH_EDITOR virtual const FText GetPaletteCategory() override; #endif protected: virtual TSharedRef RebuildWidget() override; protected: FReply GDMSlateHandleClicked(); void GDMSlateHandlePressed(); void GDMSlateHandleReleased(); void GDMSlateHandleHovered(); void GDMSlateHandleUnhovered(); }; ``` `Plugin/GameDebugMenu/Source/GameDebugMenu/Public/Widgets/GDMComboBoxString.h`: ```h /** * Copyright (c) 2020 akihiko moroi * * This software is released under the MIT License. * (See accompanying file LICENSE.txt or copy at http://opensource.org/licenses/MIT) */ #pragma once #include "CoreMinimal.h" #include "Components/ComboBoxString.h" #include "GDMComboBoxString.generated.h" /** * */ UCLASS() class GAMEDEBUGMENU_API UGDMComboBoxString : public UComboBoxString { GENERATED_BODY() public: UGDMComboBoxString(const FObjectInitializer& ObjectInitializer); public: #if WITH_EDITOR virtual const FText GetPaletteCategory() override; #endif }; ``` `Plugin/GameDebugMenu/Source/GameDebugMenu/Public/Widgets/GDMDebugReportWidget.h`: ```h /** * Copyright (c) 2020 akihiko moroi * * This software is released under the MIT License. * (See accompanying file LICENSE.txt or copy at http://opensource.org/licenses/MIT) */ #pragma once #include "CoreMinimal.h" #include "GameDebugMenuWidget.h" #include "Reports/GDMDebugReportRequester.h" #include "GDMDebugReportWidget.generated.h" DECLARE_DYNAMIC_MULTICAST_DELEGATE_OneParam(FOnReceivedReportDelegate, bool, bWasSuccessful); class UTexture2D; UCLASS() class GAMEDEBUGMENU_API UGDMDebugReportWidget : public UGameDebugMenuWidget { GENERATED_BODY() public: UPROPERTY(BlueprintAssignable) FOnReceivedReportDelegate OnReceivedReportDispatcher; UPROPERTY(BlueprintReadOnly, Transient) UTexture2D* ScreenshotTexture; UPROPERTY(BlueprintReadOnly, Transient) TArray ScreenshotImageData; UPROPERTY(BlueprintReadWrite) int32 TestCount; UPROPERTY(BlueprintReadWrite) int32 MaxTestCount; protected: FDateTime ScreenshotCapturedDateTime; TWeakObjectPtr CurrentDebugReportRequester; public: UGDMDebugReportWidget(const FObjectInitializer& ObjectInitializer); UFUNCTION(BlueprintCallable) virtual void SendDebugReport(const FString& Subject, const FString& Description,int32 IssueCategoryIndex, int32 PriorityIndex, int32 AssigneeIndex, bool bSendLogs, bool bSendScreenshotCapture); UFUNCTION(BlueprintCallable) virtual TArray GetIssueCategoryNameList(); UFUNCTION(BlueprintCallable) virtual TArray GetPriorityNameList(); UFUNCTION(BlueprintCallable) virtual TArray GetAssigneeNameList(); UFUNCTION(BlueprintPure) virtual int32 GetDefaultIssueCategoryIndex(); UFUNCTION(BlueprintPure) virtual int32 GetDefaultPriorityIndex(); virtual void OnScreenshotCaptured(int32 Width, int32 Height, const TArray& Bitmap); UFUNCTION(BlueprintImplementableEvent) void OnCreatedScreenshotTexture(UTexture2D* NewScreenshotTexture); UFUNCTION() virtual void OnRequesterDestroyed(AActor* DestroyedActor); UFUNCTION(BlueprintPure) virtual bool IsRequesting(); }; ``` `Plugin/GameDebugMenu/Source/GameDebugMenu/Public/Widgets/GDMFunctionWidget.h`: ```h /** * Copyright (c) 2020 akihiko moroi * * This software is released under the MIT License. * (See accompanying file LICENSE.txt or copy at http://opensource.org/licenses/MIT) */ #pragma once #include "CoreMinimal.h" #include "GameDebugMenuWidget.h" #include "GameDebugMenuTypes.h" #include "GDMFunctionWidget.generated.h" /** * */ UCLASS() class UGDMFunctionWidget : public UGameDebugMenuWidget { GENERATED_BODY() public: /* 関数所持オブジェクト */ UPROPERTY(BlueprintReadWrite, Category = "GDM|Functions") UObject* TargetObject; /* 関数名 */ UPROPERTY(BlueprintReadWrite, Category = "GDM|Functions") FName FunctionName; UPROPERTY(BlueprintReadWrite, Category = "GDM|Functions") FString FunctionSaveKey; public: UFUNCTION(BlueprintCallable) bool TryCallObjectFunction(FName EventName); protected: bool GDMProcessEvent(FName EventName, void* Parms); }; ``` `Plugin/GameDebugMenu/Source/GameDebugMenu/Public/Widgets/GDMIntSpinBox.h`: ```h /** * Copyright (c) 2020 akihiko moroi * * This software is released under the MIT License. * (See accompanying file LICENSE.txt or copy at http://opensource.org/licenses/MIT) */ #pragma once #include "CoreMinimal.h" #include "Components/Widget.h" #include "Fonts/SlateFontInfo.h" #include "Styling/SlateColor.h" #include "Styling/SlateTypes.h" #include "Widgets/SWidget.h" #include "Widgets/Input/SSpinBox.h" #include "GDMIntSpinBox.generated.h" /** * Int版SpinBox */ UCLASS() class UGDMIntSpinBox : public UWidget { GENERATED_UCLASS_BODY() public: DECLARE_DYNAMIC_MULTICAST_DELEGATE_OneParam(FOnSpinBoxValueChangedEvent, int32, InValue); DECLARE_DYNAMIC_MULTICAST_DELEGATE_TwoParams(FOnSpinBoxValueCommittedEvent, int32, InValue, ETextCommit::Type, CommitMethod); DECLARE_DYNAMIC_MULTICAST_DELEGATE(FOnSpinBoxBeginSliderMovement); public: /** Value stored in this spin box */ UPROPERTY(EditAnywhere, Category=Content) int32 Value; /** A bindable delegate to allow logic to drive the value of the widget */ UPROPERTY() FGetInt32 ValueDelegate; public: /** The Style */ UPROPERTY(EditAnywhere, BlueprintReadWrite, Category="Style", meta=( DisplayName="Style" )) FSpinBoxStyle WidgetStyle; UPROPERTY() USlateWidgetStyleAsset* Style_DEPRECATED; /** The amount by which to change the spin box value as the slider moves. */ UPROPERTY(EditAnywhere, BlueprintReadOnly, Category="Slider") int32 Delta; /** The exponent by which to increase the delta as the mouse moves. 1 is constant (never increases the delta). */ UPROPERTY(EditAnywhere, BlueprintReadOnly, Category="Slider") float SliderExponent; /** Font color and opacity (overrides style) */ UPROPERTY(EditAnywhere, BlueprintReadOnly, Category="Display") FSlateFontInfo Font; /** The justification the value text should appear as. */ UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "Display") TEnumAsByte Justification; /** The minimum width of the spin box */ UPROPERTY(EditAnywhere, BlueprintReadOnly, Category="Display", AdvancedDisplay, DisplayName = "Minimum Desired Width") float MinDesiredWidth; /** Whether to remove the keyboard focus from the spin box when the value is committed */ UPROPERTY(EditAnywhere, BlueprintReadOnly, Category="Input", AdvancedDisplay) bool ClearKeyboardFocusOnCommit; /** Whether to select the text in the spin box when the value is committed */ UPROPERTY(EditAnywhere, BlueprintReadOnly, Category="Input", AdvancedDisplay) bool SelectAllTextOnCommit; UPROPERTY(EditAnywhere, BlueprintReadOnly, Category="Style") FSlateColor ForegroundColor; public: /** Called when the value is changed interactively by the user */ UPROPERTY(BlueprintAssignable, Category="SpinBox|Events") FOnSpinBoxValueChangedEvent OnValueChanged; /** Called when the value is committed. Occurs when the user presses Enter or the text box loses focus. */ UPROPERTY(BlueprintAssignable, Category="SpinBox|Events") FOnSpinBoxValueCommittedEvent OnValueCommitted; /** Called right before the slider begins to move */ UPROPERTY(BlueprintAssignable, Category="SpinBox|Events") FOnSpinBoxBeginSliderMovement OnBeginSliderMovement; /** Called right after the slider handle is released by the user */ UPROPERTY(BlueprintAssignable, Category="SpinBox|Events") FOnSpinBoxValueChangedEvent OnEndSliderMovement; public: /** Get the current value of the spin box. */ UFUNCTION(BlueprintCallable, Category="Behavior") int32 GetValue() const; /** Set the value of the spin box. */ UFUNCTION(BlueprintCallable, Category="Behavior") void SetValue(int32 NewValue); public: /** Get the current minimum value that can be manually set in the spin box. */ UFUNCTION(BlueprintCallable, Category="Behavior") int32 GetMinValue() const; /** Set the minimum value that can be manually set in the spin box. */ UFUNCTION(BlueprintCallable, Category = "Behavior") void SetMinValue(int32 NewValue); /** Clear the minimum value that can be manually set in the spin box. */ UFUNCTION(BlueprintCallable, Category = "Behavior") void ClearMinValue(); /** Get the current maximum value that can be manually set in the spin box. */ UFUNCTION(BlueprintCallable, Category = "Behavior") int32 GetMaxValue() const; /** Set the maximum value that can be manually set in the spin box. */ UFUNCTION(BlueprintCallable, Category = "Behavior") void SetMaxValue(int32 NewValue); /** Clear the maximum value that can be manually set in the spin box. */ UFUNCTION(BlueprintCallable, Category = "Behavior") void ClearMaxValue(); /** Get the current minimum value that can be specified using the slider. */ UFUNCTION(BlueprintCallable, Category = "Behavior") int32 GetMinSliderValue() const; /** Set the minimum value that can be specified using the slider. */ UFUNCTION(BlueprintCallable, Category = "Behavior") void SetMinSliderValue(int32 NewValue); /** Clear the minimum value that can be specified using the slider. */ UFUNCTION(BlueprintCallable, Category = "Behavior") void ClearMinSliderValue(); /** Get the current maximum value that can be specified using the slider. */ UFUNCTION(BlueprintCallable, Category = "Behavior") int32 GetMaxSliderValue() const; /** Set the maximum value that can be specified using the slider. */ UFUNCTION(BlueprintCallable, Category = "Behavior") void SetMaxSliderValue(int32 NewValue); /** Clear the maximum value that can be specified using the slider. */ UFUNCTION(BlueprintCallable, Category = "Behavior") void ClearMaxSliderValue(); /** */ UFUNCTION(BlueprintCallable, Category = "Appearance") void SetForegroundColor(FSlateColor InForegroundColor); public: //~ Begin UWidget Interface virtual void SynchronizeProperties() override; //~ End UWidget Interface //~ Begin UVisual Interface virtual void ReleaseSlateResources(bool bReleaseChildren) override; //~ End UVisual Interface //~ Begin UObject Interface virtual void PostLoad() override; //~ End UObject Interface #if WITH_EDITOR virtual const FText GetPaletteCategory() override; #endif //~ End UWidget Interface protected: //~ Begin UWidget Interface virtual TSharedRef RebuildWidget() override; // End of UWidget void HandleOnValueChanged(int32 InValue); void HandleOnValueCommitted(int32 InValue, ETextCommit::Type CommitMethod); void HandleOnBeginSliderMovement(); void HandleOnEndSliderMovement(int32 InValue); protected: /** Whether the optional MinValue attribute of the widget is set */ UPROPERTY(EditAnywhere, Category = Content, meta=(InlineEditConditionToggle)) uint32 bOverride_MinValue : 1; /** Whether the optional MaxValue attribute of the widget is set */ UPROPERTY(EditAnywhere, Category = Content, meta=(InlineEditConditionToggle)) uint32 bOverride_MaxValue : 1; /** Whether the optional MinSliderValue attribute of the widget is set */ UPROPERTY(EditAnywhere, Category = Content, meta=(InlineEditConditionToggle)) uint32 bOverride_MinSliderValue : 1; /** Whether the optional MaxSliderValue attribute of the widget is set */ UPROPERTY(EditAnywhere, Category = Content, meta=(InlineEditConditionToggle)) uint32 bOverride_MaxSliderValue : 1; /** The minimum allowable value that can be manually entered into the spin box */ UPROPERTY(EditAnywhere, Category = Content, DisplayName = "Minimum Value", meta = (editcondition = "bOverride_MinValue")) int32 MinValue; /** The maximum allowable value that can be manually entered into the spin box */ UPROPERTY(EditAnywhere, Category = Content, DisplayName = "Maximum Value", meta = (editcondition = "bOverride_MaxValue")) int32 MaxValue; /** The minimum allowable value that can be specified using the slider */ UPROPERTY(EditAnywhere, Category = Content, DisplayName = "Minimum Slider Value", meta = (editcondition = "bOverride_MinSliderValue")) int32 MinSliderValue; /** The maximum allowable value that can be specified using the slider */ UPROPERTY(EditAnywhere, Category = Content, DisplayName = "Maximum Slider Value", meta = (editcondition = "bOverride_MaxSliderValue")) int32 MaxSliderValue; protected: TSharedPtr> MySpinBox; PROPERTY_BINDING_IMPLEMENTATION(int32, Value); }; ``` `Plugin/GameDebugMenu/Source/GameDebugMenu/Public/Widgets/GDMPropertyWidget.h`: ```h /** * Copyright (c) 2020 akihiko moroi * * This software is released under the MIT License. * (See accompanying file LICENSE.txt or copy at http://opensource.org/licenses/MIT) */ #pragma once #include "CoreMinimal.h" #include "GameDebugMenuWidget.h" #include "GameDebugMenuTypes.h" #include "GDMPropertyWidget.generated.h" /** * */ UCLASS() class UGDMPropertyWidget : public UGameDebugMenuWidget { GENERATED_BODY() public: /** プロパティ所持オブジェクト */ UPROPERTY(BlueprintReadWrite, Category = "GDM|Properties") TObjectPtr TargetObject; /** プロパティ名 */ UPROPERTY(BlueprintReadWrite, Category = "GDM|Properties") FName PropertyName; /** プロパティの種類 */ UPROPERTY(BlueprintReadWrite, Category = "GDM|Properties") EGDMPropertyType PropertyType; /** UIの設定情報 */ UPROPERTY(BlueprintReadWrite, Category = "GDM|Properties") FGDMPropertyUIConfigInfo PropertyConfigInfo; /** UI操作時のプロパティの変化量 */ UPROPERTY(BlueprintReadWrite, Category = "GDM|Properties") float ChangeAmount; UPROPERTY(BlueprintReadWrite, Category = "GDM|Properties") FString PropertySaveKey; protected: bool bStartChangeAmount; bool bChangedMaxChangeAmount; float ElapsedTime; float InactiveElapsedTime = 0.0f; public: virtual void NativeConstruct() override; virtual void NativeDestruct() override; virtual void NativeTick(const FGeometry& MyGeometry, float InDeltaTime) override; public: UFUNCTION(BlueprintCallable, Category = "GDM") virtual void StartChangeAmountTime(); UFUNCTION(BlueprintCallable, Category = "GDM") virtual void ResetChangeAmountTime(); UFUNCTION(BlueprintCallable, Category = "GDM|Properties") bool GetPropertyValue_Bool(bool& bHasProperty); UFUNCTION(BlueprintCallable, Category = "GDM|Properties") void SetPropertyValue_Bool(bool bNewValue, bool& bHasProperty); UFUNCTION(BlueprintCallable, Category = "GDM|Properties") float GetPropertyValue_Float(bool& bHasProperty); UFUNCTION(BlueprintCallable, Category = "GDM|Properties") void SetPropertyValue_Float(float NewValue, bool& bHasProperty); UFUNCTION(BlueprintCallable, Category = "GDM|Properties") int32 GetPropertyValue_Int(bool& bHasProperty); UFUNCTION(BlueprintCallable, Category = "GDM|Properties") void SetPropertyValue_Int(int32 NewValue, bool& bHasProperty); UFUNCTION(BlueprintCallable, Category = "GDM|Properties") uint8 GetPropertyValue_Byte(bool& bHasProperty); UFUNCTION(BlueprintCallable, Category = "GDM|Properties") void SetPropertyValue_Byte(uint8 NewValue, bool& bHasProperty); UFUNCTION(BlueprintCallable, Category = "GDM|Properties") TArray GetEnumDisplayNames(const FString& EnumPath, bool& bHasProperty); UFUNCTION(BlueprintCallable, Category = "GDM|Properties") FString GetPropertyValue_String(bool& bHasProperty); UFUNCTION(BlueprintCallable, Category = "GDM|Properties") void SetPropertyValue_String(FString NewValue, bool& bHasProperty); UFUNCTION(BlueprintCallable, Category = "GDM|Properties") FVector GetPropertyValue_Vector(bool& bHasProperty); UFUNCTION(BlueprintCallable, Category = "GDM|Properties") void SetPropertyValue_Vector(FVector NewValue, bool& bHasProperty); UFUNCTION(BlueprintCallable, Category = "GDM|Properties") FVector2D GetPropertyValue_Vector2D(bool& bHasProperty); UFUNCTION(BlueprintCallable, Category = "GDM|Properties") void SetPropertyValue_Vector2D(FVector2D NewValue, bool& bHasProperty); UFUNCTION(BlueprintCallable, Category = "GDM|Properties") FRotator GetPropertyValue_Rotator(bool& bHasProperty); UFUNCTION(BlueprintCallable, Category = "GDM|Properties") void SetPropertyValue_Rotator(FRotator NewValue, bool& bHasProperty); }; ``` `Plugin/GameDebugMenu/Source/GameDebugMenu/Public/Widgets/GDMTextBlock.h`: ```h /** * Copyright (c) 2020 akihiko moroi * * This software is released under the MIT License. * (See accompanying file LICENSE.txt or copy at http://opensource.org/licenses/MIT) */ #pragma once #include "CoreMinimal.h" #include "Components/TextBlock.h" #include "GDMTextBlock.generated.h" /** * */ UCLASS() class GAMEDEBUGMENU_API UGDMTextBlock : public UTextBlock { GENERATED_BODY() public: UPROPERTY(BlueprintReadOnly,EditAnywhere, Category = "GDM|Config") FString DebugMenuStringKey; #if WITH_EDITORONLY_DATA UPROPERTY(EditAnywhere, Category = "GDM|Config|Editor") FName PreviewLanguageKey; #endif public: UGDMTextBlock(const FObjectInitializer& ObjectInitializer); public: virtual void SynchronizeProperties() override; virtual void SetText(FText InText) override; UFUNCTION(BlueprintCallable, Category = "Appearance") virtual void SetWrapTextAt(float InWrapTextAt); public: #if WITH_EDITOR virtual const FText GetPaletteCategory() override; virtual bool CanEditChange(const FProperty* InProperty) const override; #endif }; ``` `Plugin/GameDebugMenu/Source/GameDebugMenu/Public/Widgets/GameDebugMenuRootWidget.h`: ```h /** * Copyright (c) 2020 akihiko moroi * * This software is released under the MIT License. * (See accompanying file LICENSE.txt or copy at http://opensource.org/licenses/MIT) */ #pragma once #include "CoreMinimal.h" #include "GameDebugMenuWidget.h" #include "GameDebugMenuRootWidget.generated.h" class AGameDebugMenuManager; /** * DebugMenuのルートに当たるWidget */ UCLASS() class GAMEDEBUGMENU_API UGameDebugMenuRootWidget : public UGameDebugMenuWidget { GENERATED_BODY() UPROPERTY(Transient) TObjectPtr Manager = nullptr; public: virtual void NativeConstruct() override; virtual void NativeDestruct() override; virtual void ActivateDebugMenu() override; virtual void DeactivateDebugMenu() override; public: void SetDebugMenuManager(AGameDebugMenuManager* InManager); UFUNCTION(BlueprintImplementableEvent) void InitializeRootWidget(); UFUNCTION(BlueprintImplementableEvent) void ShowDebugReport(); UFUNCTION(BlueprintPure) AGameDebugMenuManager* GetOwnerManager() const; UFUNCTION(BlueprintCallable) void SwitchInputComponentGroupForGameDebugMenu(FName NewGroupName); }; ``` `Plugin/GameDebugMenu/Source/GameDebugMenu/Public/Widgets/GameDebugMenuWidget.h`: ```h /** * Copyright (c) 2020 akihiko moroi * * This software is released under the MIT License. * (See accompanying file LICENSE.txt or copy at http://opensource.org/licenses/MIT) */ #pragma once #include "CoreMinimal.h" #include "Blueprint/UserWidget.h" #include "GameDebugMenuTypes.h" #include "GameDebugMenuWidget.generated.h" class UInputAction; class UGDMEnhancedInputComponent; DECLARE_DYNAMIC_MULTICAST_DELEGATE_TwoParams(FGDMWidgetDelegate, UGameDebugMenuWidget*, TargetWidget, FName, EventName); DECLARE_DYNAMIC_DELEGATE( FOnGameDebugMenuWidgetInputAction ); /** * DebugMenu用Widgetの基底クラス * ページを増やす場合はこれを継承させて作成 */ UCLASS(Abstract, Blueprintable, BlueprintType) class GAMEDEBUGMENU_API UGameDebugMenuWidget : public UUserWidget { GENERATED_BODY() public: /** イベント通知ディスパッチャー */ UPROPERTY(BlueprintAssignable) FGDMWidgetDelegate OnSendWidgetEventDispatcher; protected: bool bActivateMenu; TArray InputHandles; public: UGameDebugMenuWidget(const FObjectInitializer& ObjectInitializer); protected: virtual void CreateInputComponent() override; virtual void StartProcessingInputScriptDelegates() override; virtual void StopProcessingInputScriptDelegates() override; public: /** * UE5.7+: UUserWidget::InitializeInputComponent を override できなくなったため、 * DebugMenu用InputComponent生成は自前で行う(登録/Pushは行わない)。 */ UFUNCTION(BlueprintCallable, Category = "GDM|Input") void EnsureDebugMenuInputComponent(); /** * ローカルのコントローラーを取得(DebugCamera操作中でも取得できるもの) */ UFUNCTION(BlueprintPure, Category = "GDM") APlayerController* GetOriginalPlayerController() const; /** * Widgetが所持するInputComponentを返す */ UFUNCTION(BlueprintPure, Category = "GDM|Input") UGDMEnhancedInputComponent* GetMyInputComponent() const; /** * 指定のInputActionが入力されたときに呼び出される関数名を登録する * @param Action - 対象となる入力アクション * @param FunctionName - 呼び出される関数名 * @param TriggerEvent - 入力イベント * @param FunctionObject - 関数名を持つオブジェクト。nullなら自分自身 */ UFUNCTION(BlueprintCallable, Category = "GDM|Input", meta = (AdvancedDisplay = "2")) bool RegisterDebugMenuWidgetInputFunction(const UInputAction* Action, const FName FunctionName, const ETriggerEvent TriggerEvent = ETriggerEvent::Triggered, UObject* FunctionObject = nullptr); /** * 指定のInputActionが入力されたときに呼び出されるイベントを登録する * @param Action - 対象となる入力アクション * @param Callback - 入力時に呼び出されるイベント * @param TriggerEvent - 入力イベント */ UFUNCTION(BlueprintCallable, Category = "GDM|Input", meta = (AdvancedDisplay = "2")) bool RegisterDebugMenuWidgetInputEvent(const UInputAction* Action, FOnGameDebugMenuWidgetInputAction Callback, const ETriggerEvent TriggerEvent = ETriggerEvent::Triggered); /** * 登録した入力イベントを解除する */ UFUNCTION(BlueprintCallable, Category = "GDM|Input") void UnregisterDebugMenuWidgetInputs(); /** * イベント通知 */ UFUNCTION(BlueprintCallable, Category = "GDM|Event") void SendSelfEvent(FName EventName); /** * コンソールコマンドを実行する */ UFUNCTION(BlueprintCallable, Category = "GDM|Command") void ExecuteGDMConsoleCommand(const FString Command, const EGDMConsoleCommandNetType CommandNetType); /** * UIを表示するときのイベント * @param bRequestDebugMenuManager - マネージャーから呼び出された場合trueになる。 * @note 呼ばれるのはActivateされてるときのみ */ UFUNCTION(BlueprintCallable, BlueprintImplementableEvent, Category = "GDM|Event", meta = (AdvancedDisplay ="bRequestDebugMenuManager")) void OnShowingMenu(bool bRequestDebugMenuManager); /** * UIを非表示にするときのイベント * @param bRequestDebugMenuManager - マネージャーから呼び出された場合trueになる。 * @note 呼ばれるのはActivateされてるときのみ */ UFUNCTION(BlueprintCallable, BlueprintImplementableEvent, Category = "GDM|Event", meta = (AdvancedDisplay ="bRequestDebugMenuManager")) void OnHidingMenu(bool bRequestDebugMenuManager); /** * アクティベート化をするときに呼ばれるイベント * @note ここで表示したり、操作できる状態に移行する */ UFUNCTION(BlueprintImplementableEvent, Category = "GDM|Event") void OnActivateDebugMenu(); /** * ディアクティベート化をするときに呼ばれるイベント * @note ここで非表示にしたり、メニューの終了処理を行う */ UFUNCTION(BlueprintImplementableEvent, Category = "GDM|Event") void OnDeactivateDebugMenu(); /** * 画面を最新の状態し直したいときに呼ばれるイベント * @note UI上で参照するデータを取得し直すなどをここでする */ UFUNCTION(BlueprintCallable, BlueprintImplementableEvent, Category = "GDM|Event") void OnRefreshDataAndDisplay(); /** * アクティベート状態であればTrue */ UFUNCTION(BlueprintPure, Category = "GDM|Event") virtual bool IsActivateDebugMenu(); /** * UIのアクティベート化をする(表示、操作できる状態になる) */ UFUNCTION(BlueprintCallable, Category = "GDM|Event") virtual void ActivateDebugMenu(); /** * UIのディアクティベート化をする(非表示、操作不可状態になる) */ UFUNCTION(BlueprintCallable, Category = "GDM|Event") virtual void DeactivateDebugMenu(); UFUNCTION() virtual void OnChangeDebugMenuLanguage(const FName& NewLanguageKey, const FName& OldLanguageKey); /** * デバックメニューの使用言語が変更されたら呼ばれる */ UFUNCTION(BlueprintImplementableEvent, Category = "GDM|Language") void OnChangeDebugMenuLanguageBP(const FName& NewLanguageKey, const FName& OldLanguageKey); /** * 指定クラスの子供Widgetをすべて取得する * @param WidgetClass - 対象のクラス * @param OutChildWidgets - 取得できたWidget * @param bEndSearchAsYouFind - 最初に見つけた時点で終了する * @return true: 1つ以上OutChildWidgetsがある false: 1つもWidgetがなかった */ UFUNCTION(BlueprintCallable, Category = "GDM", meta = (DeterminesOutputType = "WidgetClass", DynamicOutputParam = "OutChildWidgets")) virtual bool GetWidgetChildrenOfClass(TSubclassOf WidgetClass, TArray& OutChildWidgets, bool bEndSearchAsYouFind); }; ``` `Plugin/GameDebugMenu/Source/GameDebugMenuEditor/GameDebugMenuEditor.Build.cs`: ```cs /** * Copyright (c) 2020 akihiko moroi * * This software is released under the MIT License. * (See accompanying file LICENSE.txt or copy at http://opensource.org/licenses/MIT) */ using UnrealBuildTool; public class GameDebugMenuEditor : ModuleRules { public GameDebugMenuEditor(ReadOnlyTargetRules Target) : base(Target) { PrivateIncludePaths.AddRange( new string[] { "GameDebugMenuEditor/Private", "GameDebugMenuEditor/Private/AssetTypeActions", "GameDebugMenuEditor/Private/DetailCustomizations", "GameDebugMenuEditor/Private/Factory", "GameDebugMenuEditor/Private/Pins", } ); PublicDependencyModuleNames.AddRange( new string[] { "Core", "UMG", "UnrealEd", "UMGEditor", "GameDebugMenu", } ); PrivateDependencyModuleNames.AddRange( new string[] { "CoreUObject", "Engine", "Slate", "SlateCore", "GraphEditor", "BlueprintGraph", } ); } } ``` `Plugin/GameDebugMenu/Source/GameDebugMenuEditor/Private/AssetTypeActions/AssetTypeActions_GDMPlayerControllerProxyComponent.cpp`: ```cpp /** * Copyright (c) 2020 akihiko moroi * * This software is released under the MIT License. * (See accompanying file LICENSE.txt or copy at http://opensource.org/licenses/MIT) */ #include "AssetTypeActions/AssetTypeActions_GDMPlayerControllerProxyComponent.h" #include "GameDebugMenuEditor.h" #include "Component/GDMPlayerControllerProxyComponent.h" FText FAssetTypeActions_GDMPlayerControllerProxyComponent::GetName() const { return NSLOCTEXT("AssetTypeActions", "AssetTypeActions_GDMPlayerControllerProxyComponent", "GDM Player Controller Proxy Component"); } FColor FAssetTypeActions_GDMPlayerControllerProxyComponent::GetTypeColor() const { return FColor(60, 60, 60); } UClass* FAssetTypeActions_GDMPlayerControllerProxyComponent::GetSupportedClass() const { return UGDMPlayerControllerProxyComponent::StaticClass(); } uint32 FAssetTypeActions_GDMPlayerControllerProxyComponent::GetCategories() { return FGameDebugMenuEditorModule::GetAssetCategory(); } bool FAssetTypeActions_GDMPlayerControllerProxyComponent::CanLocalize() const { return false; } FText FAssetTypeActions_GDMPlayerControllerProxyComponent::GetAssetDescription(const FAssetData& AssetData) const { return FText::FromString(FString(TEXT("GDM Player Controller Proxy Component"))); } ``` `Plugin/GameDebugMenu/Source/GameDebugMenuEditor/Private/AssetTypeActions/AssetTypeActions_GameDebugMenuManager.cpp`: ```cpp /** * Copyright (c) 2020 akihiko moroi * * This software is released under the MIT License. * (See accompanying file LICENSE.txt or copy at http://opensource.org/licenses/MIT) */ #include "AssetTypeActions/AssetTypeActions_GameDebugMenuManager.h" #include "GameDebugMenuManager.h" #include "GameDebugMenuEditor.h" FText FAssetTypeActions_GameDebugMenuManager::GetName() const { return NSLOCTEXT("AssetTypeActions", "AssetTypeActions_GameDebugMenuManager", "Game Debug Menu Manager"); } FColor FAssetTypeActions_GameDebugMenuManager::GetTypeColor() const { return FColor(60, 60, 60); } UClass* FAssetTypeActions_GameDebugMenuManager::GetSupportedClass() const { return AGameDebugMenuManager::StaticClass(); } uint32 FAssetTypeActions_GameDebugMenuManager::GetCategories() { return FGameDebugMenuEditorModule::GetAssetCategory(); } bool FAssetTypeActions_GameDebugMenuManager::CanLocalize() const { return false; } FText FAssetTypeActions_GameDebugMenuManager::GetAssetDescription(const FAssetData& AssetData) const { return FText::FromString(FString(TEXT("Game Debug Menu Manager"))); } ``` `Plugin/GameDebugMenu/Source/GameDebugMenuEditor/Private/AssetTypeActions/AssetTypeActions_GameDebugMenuWidget.cpp`: ```cpp /** * Copyright (c) 2020 akihiko moroi * * This software is released under the MIT License. * (See accompanying file LICENSE.txt or copy at http://opensource.org/licenses/MIT) */ #include "AssetTypeActions/AssetTypeActions_GameDebugMenuWidget.h" #include "GameDebugMenuEditor.h" #include "Widgets/GameDebugMenuWidget.h" FText FAssetTypeActions_GameDebugMenuWidget::GetName() const { return NSLOCTEXT("AssetTypeActions", "AssetTypeActions_GameDebugMenuWidget", "Game Debug Menu Widget"); } FColor FAssetTypeActions_GameDebugMenuWidget::GetTypeColor() const { return FColor(60, 60, 60); } UClass* FAssetTypeActions_GameDebugMenuWidget::GetSupportedClass() const { return UGameDebugMenuWidget::StaticClass(); } uint32 FAssetTypeActions_GameDebugMenuWidget::GetCategories() { return FGameDebugMenuEditorModule::GetAssetCategory(); } bool FAssetTypeActions_GameDebugMenuWidget::CanLocalize() const { return false; } FText FAssetTypeActions_GameDebugMenuWidget::GetAssetDescription(const FAssetData& AssetData) const { return FText::FromString(FString(TEXT("Game Debug Menu Widget"))); } ``` `Plugin/GameDebugMenu/Source/GameDebugMenuEditor/Private/DetailCustomizations/GDMGameplayCategoryKeyCustomization.cpp`: ```cpp /** * Copyright (c) 2020 akihiko moroi * * This software is released under the MIT License. * (See accompanying file LICENSE.txt or copy at http://opensource.org/licenses/MIT) */ #include "GDMGameplayCategoryKeyCustomization.h" #include "PropertyHandle.h" #include "DetailWidgetRow.h" #include "Widgets/DeclarativeSyntaxSupport.h" #include "Widgets/Input/STextComboBox.h" #include "GameDebugMenuSettings.h" #define LOCTEXT_NAMESPACE "GDMGameplayCategoryKeyCustomization" /************************************************************************/ /* FGOAPStateKeyCustomization */ /************************************************************************/ TSharedRef FGDMGameplayCategoryKeyCustomization::MakeInstance() { return MakeShareable(new FGDMGameplayCategoryKeyCustomization()); } void FGDMGameplayCategoryKeyCustomization::CustomizeHeader(TSharedRef PropertyHandle, FDetailWidgetRow& HeaderRow, IPropertyTypeCustomizationUtils& CustomizationUtils) { MakeGameplayCategoryNames(); uint32 NumChildren; PropertyHandle->GetNumChildren(NumChildren); for( uint32 ChildIndex = 0; ChildIndex < NumChildren; ++ChildIndex ) { const TSharedPtr ChildHandle = PropertyHandle->GetChildHandle(ChildIndex); if( ChildHandle->GetProperty()->GetName() == TEXT("Index") ) { IndexHandle = ChildHandle; int32 CategoryIndex = 0; ChildHandle->GetValue(CategoryIndex); ArrayIndex = GetArrayIndex(CategoryIndex); break; } } if( ArrayIndex >= GameplayCategoryNames.Num() ) { ArrayIndex = 0; } check(IndexHandle.IsValid()); HeaderRow .NameContent() [ PropertyHandle->CreatePropertyNameWidget() ] .ValueContent() .MinDesiredWidth(500) [ SNew(SHorizontalBox) + SHorizontalBox::Slot() .HAlign(HAlign_Left) [ SAssignNew(TextComboBox, STextComboBox) .OptionsSource(&GameplayCategoryNames) .OnSelectionChanged(this, &FGDMGameplayCategoryKeyCustomization::OnSelectionChanged) .InitiallySelectedItem(GameplayCategoryNames[ArrayIndex]) ] ]; } void FGDMGameplayCategoryKeyCustomization::MakeGameplayCategoryNames() { GameplayCategoryNames.Reset(); UGameDebugMenuSettings* Settings = GetMutableDefault(); for( int32 Index = 0; Index < Settings->OrderGameplayCategoryTitles.Num(); ++Index ) { GameplayCategoryNames.Add(MakeShareable(new FString(Settings->GetGameplayCategoryTitle(Index)))); } } void FGDMGameplayCategoryKeyCustomization::OnSelectionChanged(TSharedPtr ItemSelected, ESelectInfo::Type SelectInfo) { if( ItemSelected.IsValid() ) { for( int32 Index = 0; Index < GameplayCategoryNames.Num(); ++Index ) { if( GameplayCategoryNames[Index] == ItemSelected ) { ArrayIndex = Index; break; } } UGameDebugMenuSettings* Settings = GetMutableDefault(); IndexHandle->SetValue(Settings->GetGameplayCategoryIndex(ArrayIndex)); } } int32 FGDMGameplayCategoryKeyCustomization::GetArrayIndex(int32 CategoryIndex) { int32 ReturnValue = INDEX_NONE; UGameDebugMenuSettings* Settings = GetMutableDefault(); for( int32 Idx = 0; Idx < Settings->OrderGameplayCategoryTitles.Num(); ++Idx ) { if( Settings->OrderGameplayCategoryTitles[Idx].Index == CategoryIndex ) { ReturnValue = Idx; break; } } if( ReturnValue == INDEX_NONE ) { ReturnValue = 0; } return ReturnValue; } #undef LOCTEXT_NAMESPACE ``` `Plugin/GameDebugMenu/Source/GameDebugMenuEditor/Private/DetailCustomizations/GDMGameplayCategoryKeyCustomization.h`: ```h /** * Copyright (c) 2020 akihiko moroi * * This software is released under the MIT License. * (See accompanying file LICENSE.txt or copy at http://opensource.org/licenses/MIT) */ #pragma once #include "IPropertyTypeCustomization.h" #include class FGDMGameplayCategoryKeyCustomization : public IPropertyTypeCustomization { protected: TArray> GameplayCategoryNames; TSharedPtr IndexHandle; TSharedPtr TextComboBox; int32 ArrayIndex = 0; public: static TSharedRef MakeInstance(); /* Begin IPropertyTypeCustomization */ virtual void CustomizeHeader(TSharedRef PropertyHandle, FDetailWidgetRow& HeaderRow, IPropertyTypeCustomizationUtils& CustomizationUtils) override; virtual void CustomizeChildren(TSharedRef PropertyHandle, IDetailChildrenBuilder& ChildBuilder, IPropertyTypeCustomizationUtils& CustomizationUtils) override {}; /* End IPropertyTypeCustomization */ private: void MakeGameplayCategoryNames(); void OnSelectionChanged(TSharedPtr ItemSelected, ESelectInfo::Type SelectInfo); int32 GetArrayIndex(int32 CategoryIndex); }; ``` `Plugin/GameDebugMenu/Source/GameDebugMenuEditor/Private/Factory/GDMPlayerControllerProxyComponentFactory.cpp`: ```cpp /** * Copyright (c) 2020 akihiko moroi * * This software is released under the MIT License. * (See accompanying file LICENSE.txt or copy at http://opensource.org/licenses/MIT) */ #include "Factory/GDMPlayerControllerProxyComponentFactory.h" #include "Component/GDMPlayerControllerProxyComponent.h" #include "Kismet2/KismetEditorUtilities.h" #define LOCTEXT_NAMESPACE "GameDebugMenu" UGDMPlayerControllerProxyComponentFactory::UGDMPlayerControllerProxyComponentFactory(const class FObjectInitializer& Object) : Super(Object) { SupportedClass = UGDMPlayerControllerProxyComponent::StaticClass(); bEditAfterNew = true; bCreateNew = true; } UObject* UGDMPlayerControllerProxyComponentFactory::FactoryCreateNew(UClass* Class, UObject* InParent, FName Name, EObjectFlags Flags, UObject* Context, FFeedbackContext* Warn) { check(Class->IsChildOf(UGDMPlayerControllerProxyComponent::StaticClass())); UBlueprint* Blueprint = FKismetEditorUtilities::CreateBlueprint(Class, InParent, Name, BPTYPE_Normal, UBlueprint::StaticClass(), UBlueprintGeneratedClass::StaticClass(), TEXT("AssetTypeActions")); return Blueprint; } #undef LOCTEXT_NAMESPACE ``` `Plugin/GameDebugMenu/Source/GameDebugMenuEditor/Private/Factory/GameDebugMenuManagerFactory.cpp`: ```cpp /** * Copyright (c) 2020 akihiko moroi * * This software is released under the MIT License. * (See accompanying file LICENSE.txt or copy at http://opensource.org/licenses/MIT) */ #include "Factory/GameDebugMenuManagerFactory.h" #include "Kismet2/KismetEditorUtilities.h" #include "GameDebugMenuManager.h" #define LOCTEXT_NAMESPACE "GameDebugMenu" UGameDebugMenuManagerFactory::UGameDebugMenuManagerFactory(const class FObjectInitializer& Object) : Super(Object) { SupportedClass = AGameDebugMenuManager::StaticClass(); bEditAfterNew = true; bCreateNew = true; } UObject* UGameDebugMenuManagerFactory::FactoryCreateNew(UClass* Class, UObject* InParent, FName Name, EObjectFlags Flags, UObject* Context, FFeedbackContext* Warn) { check(Class->IsChildOf(AGameDebugMenuManager::StaticClass())); return FKismetEditorUtilities::CreateBlueprint(Class, InParent, Name, BPTYPE_Normal, UBlueprint::StaticClass(), UBlueprintGeneratedClass::StaticClass(), TEXT("AssetTypeActions")); } #undef LOCTEXT_NAMESPACE ``` `Plugin/GameDebugMenu/Source/GameDebugMenuEditor/Private/Factory/GameDebugMenuWidgetFactory.cpp`: ```cpp /** * Copyright (c) 2020 akihiko moroi * * This software is released under the MIT License. * (See accompanying file LICENSE.txt or copy at http://opensource.org/licenses/MIT) */ #include "Factory/GameDebugMenuWidgetFactory.h" #include "WidgetBlueprint.h" #include "Widgets/GameDebugMenuWidget.h" #include "Kismet2/KismetEditorUtilities.h" #define LOCTEXT_NAMESPACE "GameDebugMenu" UGameDebugMenuWidgetFactory::UGameDebugMenuWidgetFactory(const class FObjectInitializer& Object) : Super(Object) { SupportedClass = UGameDebugMenuWidget::StaticClass(); bEditAfterNew = true; bCreateNew = true; } UObject* UGameDebugMenuWidgetFactory::FactoryCreateNew(UClass* Class, UObject* InParent, FName Name, EObjectFlags Flags, UObject* Context, FFeedbackContext* Warn) { check(Class->IsChildOf(UGameDebugMenuWidget::StaticClass())); UBlueprint* Blueprint = FKismetEditorUtilities::CreateBlueprint(Class, InParent, Name, BPTYPE_Normal, UWidgetBlueprint::StaticClass(), UBlueprintGeneratedClass::StaticClass(), TEXT("AssetTypeActions")); return Blueprint; } #undef LOCTEXT_NAMESPACE ``` `Plugin/GameDebugMenu/Source/GameDebugMenuEditor/Private/GameDebugMenuEditor.cpp`: ```cpp /** * Copyright (c) 2020 akihiko moroi * * This software is released under the MIT License. * (See accompanying file LICENSE.txt or copy at http://opensource.org/licenses/MIT) */ #include "GameDebugMenuEditor.h" #include "ISettingsModule.h" #include "GameDebugMenuSettings.h" #include #include "AssetTypeActions/AssetTypeActions_GameDebugMenuManager.h" #include "AssetTypeActions/AssetTypeActions_GDMPlayerControllerProxyComponent.h" #include "AssetTypeActions/AssetTypeActions_GameDebugMenuWidget.h" #include #include #include "GDMGameplayCategoryKeyCustomization.h" #include "Pins/GDMGameplayCategoryKeyPinFactory.h" #define LOCTEXT_NAMESPACE "FGameDebugMenuEditorModule" EAssetTypeCategories::Type FGameDebugMenuEditorModule::GDMAssetCategory; void FGameDebugMenuEditorModule::StartupModule() { IAssetTools& AssetTools = FModuleManager::LoadModuleChecked("AssetTools").Get(); GDMAssetCategory = AssetTools.RegisterAdvancedAssetCategory(FName(TEXT("GameDebugMenu")), LOCTEXT("GameDebugMenuAssetsCategory", "Game Debug Menu")); RegisterAssetTypeAction(AssetTools, MakeShareable(new FAssetTypeActions_GameDebugMenuManager())); RegisterAssetTypeAction(AssetTools, MakeShareable(new FAssetTypeActions_GDMPlayerControllerProxyComponent())); RegisterAssetTypeAction(AssetTools, MakeShareable(new FAssetTypeActions_GameDebugMenuWidget())); FPropertyEditorModule& PropertyModule = FModuleManager::LoadModuleChecked("PropertyEditor"); PropertyModule.RegisterCustomPropertyTypeLayout("GDMGameplayCategoryKey", FOnGetPropertyTypeCustomizationInstance::CreateStatic(&FGDMGameplayCategoryKeyCustomization::MakeInstance)); GameplayCategoryKeyPinFactory = MakeShareable(new FGDMGameplayCategoryKeyPinFactory()); FEdGraphUtilities::RegisterVisualPinFactory(GameplayCategoryKeyPinFactory); } void FGameDebugMenuEditorModule::ShutdownModule() { if (FModuleManager::Get().IsModuleLoaded("AssetTools")) { IAssetTools& AssetTools = FModuleManager::GetModuleChecked("AssetTools").Get(); for (int32 Index = 0; Index < CreatedAssetTypeActions.Num(); ++Index) { AssetTools.UnregisterAssetTypeActions(CreatedAssetTypeActions[Index].ToSharedRef()); } } CreatedAssetTypeActions.Empty(); FPropertyEditorModule& PropertyModule = FModuleManager::LoadModuleChecked("PropertyEditor"); PropertyModule.UnregisterCustomPropertyTypeLayout("GDMGameplayCategoryKey"); FEdGraphUtilities::UnregisterVisualPinFactory(GameplayCategoryKeyPinFactory); GameplayCategoryKeyPinFactory = nullptr; } EAssetTypeCategories::Type FGameDebugMenuEditorModule::GetAssetCategory() { return GDMAssetCategory; } void FGameDebugMenuEditorModule::RegisterAssetTypeAction(IAssetTools& AssetTools, TSharedRef Action) { AssetTools.RegisterAssetTypeActions(Action); CreatedAssetTypeActions.Add(Action); } #undef LOCTEXT_NAMESPACE IMPLEMENT_MODULE(FGameDebugMenuEditorModule, GameDebugMenuEditor) ``` `Plugin/GameDebugMenu/Source/GameDebugMenuEditor/Private/Pins/GDMGameplayCategoryKeyPin.cpp`: ```cpp /** * Copyright (c) 2020 akihiko moroi * * This software is released under the MIT License. * (See accompanying file LICENSE.txt or copy at http://opensource.org/licenses/MIT) */ #include "GDMGameplayCategoryKeyPin.h" #include "EdGraph/EdGraphPin.h" #include "EdGraph/EdGraphSchema.h" #include "Widgets/Input/STextComboBox.h" #include "GameDebugMenuSettings.h" #include "GameDebugMenuTypes.h" /************************************************************************/ /* SGoapStateKeyPin */ /************************************************************************/ void SGDMGameplayCategoryKeyPin::Construct(const FArguments& InArgs, UEdGraphPin* InGraphPinObj) { SGraphPin::Construct(SGraphPin::FArguments(), InGraphPinObj); } TSharedRef SGDMGameplayCategoryKeyPin::GetDefaultValueWidget() { MakeGameplayCategoryNames(); ArrayIndex = GetCategoryNameArrayIndex(); TextComboBox = SNew(STextComboBox) .Visibility(this, &SGDMGameplayCategoryKeyPin::GetStateKeyVisibility) .OptionsSource(&GameplayCategoryNames) .OnSelectionChanged(this, &SGDMGameplayCategoryKeyPin::OnSelectionChanged) .InitiallySelectedItem(GameplayCategoryNames[ArrayIndex]); return SNew(SHorizontalBox) + SHorizontalBox::Slot() .HAlign(HAlign_Left) [ TextComboBox.ToSharedRef() ]; } void SGDMGameplayCategoryKeyPin::MakeGameplayCategoryNames() { GameplayCategoryNames.Empty(); UGameDebugMenuSettings* Settings = GetMutableDefault(); for( int32 Index = 0; Index < Settings->OrderGameplayCategoryTitles.Num(); ++Index ) { GameplayCategoryNames.Add(MakeShareable(new FString(Settings->GetGameplayCategoryTitle(Index)))); } } int32 SGDMGameplayCategoryKeyPin::GetCategoryNameArrayIndex() const { int32 GraphPinValue = 0; const FString CurrentDefault = GraphPinObj->GetDefaultAsString(); if( CurrentDefault.Len() > 0 ) { constexpr int32 StartIndex = 7;/* (Index= */ int32 EndIndex; CurrentDefault.FindChar(',', EndIndex); const FString DefaultValString = CurrentDefault.Mid(StartIndex, EndIndex - StartIndex); GraphPinValue = FCString::Atoi(*DefaultValString); } int32 Index = 0; UGameDebugMenuSettings* Settings = GetMutableDefault(); for( const FGDMOrderMenuCategoryTitle& CategoryTitle : Settings->OrderGameplayCategoryTitles ) { if( CategoryTitle.Index == GraphPinValue ) { return Index; } Index++; } return 0; } void SGDMGameplayCategoryKeyPin::SetCategoryValue(const int32 InArrayIndex) const { UGameDebugMenuSettings* Settings = GetMutableDefault(); FString StrKey; StrKey.AppendInt(Settings->GetGameplayCategoryIndex(InArrayIndex)); const FString NewValue = TEXT("(Index=") + StrKey + TEXT(",KeyName=)"); GraphPinObj->GetSchema()->TrySetDefaultValue(*GraphPinObj, NewValue); } void SGDMGameplayCategoryKeyPin::OnSelectionChanged(TSharedPtr ItemSelected, ESelectInfo::Type SelectInfo) { if( ItemSelected.IsValid() ) { for( int32 Index = 0; Index < GameplayCategoryNames.Num(); ++Index ) { if( GameplayCategoryNames[Index] == ItemSelected ) { ArrayIndex = Index; break; } } } SetCategoryValue(ArrayIndex); } EVisibility SGDMGameplayCategoryKeyPin::GetStateKeyVisibility() const { return IsConnected() ? EVisibility::Collapsed : EVisibility::Visible; } ``` `Plugin/GameDebugMenu/Source/GameDebugMenuEditor/Private/Pins/GDMGameplayCategoryKeyPin.h`: ```h /** * Copyright (c) 2020 akihiko moroi * * This software is released under the MIT License. * (See accompanying file LICENSE.txt or copy at http://opensource.org/licenses/MIT) */ #pragma once #include "SlateBasics.h" #include "SGraphPin.h" /************************************************************************ /* SGDMGameplayCategoryKeyPin */ /************************************************************************/ class SGDMGameplayCategoryKeyPin : public SGraphPin { int32 ArrayIndex = 0; TArray> GameplayCategoryNames; TSharedPtr TextComboBox; public: SLATE_BEGIN_ARGS(SGDMGameplayCategoryKeyPin) {} SLATE_END_ARGS() public: void Construct(const FArguments& InArgs, UEdGraphPin* InGraphPinObj); virtual TSharedRef GetDefaultValueWidget() override; private: void MakeGameplayCategoryNames(); int32 GetCategoryNameArrayIndex() const; void SetCategoryValue(const int32 InArrayIndex) const; void OnSelectionChanged(TSharedPtr ItemSelected, ESelectInfo::Type SelectInfo); EVisibility GetStateKeyVisibility() const; }; ``` `Plugin/GameDebugMenu/Source/GameDebugMenuEditor/Private/Pins/GDMGameplayCategoryKeyPinFactory.h`: ```h /** * Copyright (c) 2020 akihiko moroi * * This software is released under the MIT License. * (See accompanying file LICENSE.txt or copy at http://opensource.org/licenses/MIT) */ #pragma once #include "SlateBasics.h" #include "EdGraph/EdGraphPin.h" #include "EdGraphSchema_K2.h" #include "EdGraphUtilities.h" #include "GDMGameplayCategoryKeyPin.h" class FGDMGameplayCategoryKeyPinFactory : public FGraphPanelPinFactory { /* 登録したオブジェクトなら自作ピンを返す */ virtual TSharedPtr CreatePin(class UEdGraphPin* InPin) const override { const UEdGraphSchema_K2* K2Schema = GetDefault(); if (InPin->PinType.PinSubCategoryObject == FGDMGameplayCategoryKey::StaticStruct()) { return SNew(SGDMGameplayCategoryKeyPin, InPin); } return nullptr; } }; ``` `Plugin/GameDebugMenu/Source/GameDebugMenuEditor/Public/AssetTypeActions/AssetTypeActions_GDMPlayerControllerProxyComponent.h`: ```h /** * Copyright (c) 2020 akihiko moroi * * This software is released under the MIT License. * (See accompanying file LICENSE.txt or copy at http://opensource.org/licenses/MIT) */ #pragma once #include "CoreMinimal.h" #include "AssetTypeActions_Base.h" class FAssetTypeActions_GDMPlayerControllerProxyComponent : public FAssetTypeActions_Base { public: /* Begin IAssetTypeActions */ virtual FText GetName() const override; virtual FColor GetTypeColor() const override; virtual UClass* GetSupportedClass() const override; virtual uint32 GetCategories() override; virtual bool CanLocalize() const override; virtual FText GetAssetDescription(const FAssetData& AssetData) const override; /* End IAssetTypeActions */ }; ``` `Plugin/GameDebugMenu/Source/GameDebugMenuEditor/Public/AssetTypeActions/AssetTypeActions_GameDebugMenuManager.h`: ```h /** * Copyright (c) 2020 akihiko moroi * * This software is released under the MIT License. * (See accompanying file LICENSE.txt or copy at http://opensource.org/licenses/MIT) */ #pragma once #include "CoreMinimal.h" #include "AssetTypeActions_Base.h" class FAssetTypeActions_GameDebugMenuManager : public FAssetTypeActions_Base { public: /* Begin IAssetTypeActions */ virtual FText GetName() const override; virtual FColor GetTypeColor() const override; virtual UClass* GetSupportedClass() const override; virtual uint32 GetCategories() override; virtual bool CanLocalize() const override; virtual FText GetAssetDescription(const FAssetData& AssetData) const override; /* End IAssetTypeActions */ }; ``` `Plugin/GameDebugMenu/Source/GameDebugMenuEditor/Public/AssetTypeActions/AssetTypeActions_GameDebugMenuWidget.h`: ```h /** * Copyright (c) 2020 akihiko moroi * * This software is released under the MIT License. * (See accompanying file LICENSE.txt or copy at http://opensource.org/licenses/MIT) */ #pragma once #include "CoreMinimal.h" #include "AssetTypeActions_Base.h" class FAssetTypeActions_GameDebugMenuWidget : public FAssetTypeActions_Base { public: /* Begin IAssetTypeActions */ virtual FText GetName() const override; virtual FColor GetTypeColor() const override; virtual UClass* GetSupportedClass() const override; virtual uint32 GetCategories() override; virtual bool CanLocalize() const override; virtual FText GetAssetDescription(const FAssetData& AssetData) const override; /* End IAssetTypeActions */ }; ``` `Plugin/GameDebugMenu/Source/GameDebugMenuEditor/Public/Factory/GDMPlayerControllerProxyComponentFactory.h`: ```h /** * Copyright (c) 2020 akihiko moroi * * This software is released under the MIT License. * (See accompanying file LICENSE.txt or copy at http://opensource.org/licenses/MIT) */ #pragma once #include "Factories/Factory.h" #include "GDMPlayerControllerProxyComponentFactory.generated.h" #define LOCTEXT_NAMESPACE "GameDebugMenu" /** * */ UCLASS() class GAMEDEBUGMENUEDITOR_API UGDMPlayerControllerProxyComponentFactory : public UFactory { GENERATED_BODY() public: UGDMPlayerControllerProxyComponentFactory(const class FObjectInitializer& Object); virtual UObject* FactoryCreateNew(UClass* Class, UObject* InParent, FName Name, EObjectFlags Flags, UObject* Context, FFeedbackContext* Warn) override; }; #undef LOCTEXT_NAMESPACE ``` `Plugin/GameDebugMenu/Source/GameDebugMenuEditor/Public/Factory/GameDebugMenuManagerFactory.h`: ```h /** * Copyright (c) 2020 akihiko moroi * * This software is released under the MIT License. * (See accompanying file LICENSE.txt or copy at http://opensource.org/licenses/MIT) */ #pragma once #include "Factories/Factory.h" #include "GameDebugMenuManagerFactory.generated.h" #define LOCTEXT_NAMESPACE "GameDebugMenu" /** * */ UCLASS() class GAMEDEBUGMENUEDITOR_API UGameDebugMenuManagerFactory : public UFactory { GENERATED_BODY() public: UGameDebugMenuManagerFactory(const class FObjectInitializer& Object); virtual UObject* FactoryCreateNew(UClass* Class, UObject* InParent, FName Name, EObjectFlags Flags, UObject* Context, FFeedbackContext* Warn) override; }; #undef LOCTEXT_NAMESPACE ``` `Plugin/GameDebugMenu/Source/GameDebugMenuEditor/Public/Factory/GameDebugMenuWidgetFactory.h`: ```h /** * Copyright (c) 2020 akihiko moroi * * This software is released under the MIT License. * (See accompanying file LICENSE.txt or copy at http://opensource.org/licenses/MIT) */ #pragma once #include "Factories/Factory.h" #include "GameDebugMenuWidgetFactory.generated.h" #define LOCTEXT_NAMESPACE "GameDebugMenu" /** * */ UCLASS() class GAMEDEBUGMENUEDITOR_API UGameDebugMenuWidgetFactory : public UFactory { GENERATED_BODY() public: UGameDebugMenuWidgetFactory(const class FObjectInitializer& Object); virtual UObject* FactoryCreateNew(UClass* Class, UObject* InParent, FName Name, EObjectFlags Flags, UObject* Context, FFeedbackContext* Warn) override; }; #undef LOCTEXT_NAMESPACE ``` `Plugin/GameDebugMenu/Source/GameDebugMenuEditor/Public/GameDebugMenuEditor.h`: ```h /** * Copyright (c) 2020 akihiko moroi * * This software is released under the MIT License. * (See accompanying file LICENSE.txt or copy at http://opensource.org/licenses/MIT) */ #pragma once #include "Modules/ModuleManager.h" #include class IAssetTools; class IAssetTypeActions; class FGDMGameplayCategoryKeyPinFactory; class FGameDebugMenuEditorModule : public IModuleInterface { private: static EAssetTypeCategories::Type GDMAssetCategory; TArray< TSharedPtr > CreatedAssetTypeActions; TSharedPtr GameplayCategoryKeyPinFactory; public: /* Begin IModuleInterface */ virtual void StartupModule() override; virtual void ShutdownModule() override; /* End IModuleInterface */ public: static EAssetTypeCategories::Type GetAssetCategory(); private: void RegisterAssetTypeAction(IAssetTools& AssetTools, TSharedRef Action); }; ``` `README.md`: ```md # GameDebugMenu Debug Menu for UnrealEngine5 ![image](https://user-images.githubusercontent.com/12130355/162192912-c876ac8f-6475-4319-adf6-cb952955dbe1.png) ## License MIT ``` `SampleProject/Config/DefaultEditor.ini`: ```ini [UnrealEd.SimpleMap] SimpleMapName=/Game/TP_ThirdPerson/Maps/ThirdPersonExampleMap [EditoronlyBP] bAllowClassAndBlueprintPinMatching=true bReplaceBlueprintWithClass= true bDontLoadBlueprintOutsideEditor= true bBlueprintIsNotBlueprintType= true ``` `SampleProject/Config/DefaultEditorPerProjectUserSettings.ini`: ```ini [ContentBrowser] ContentBrowserTab1.SelectedPaths=/Game/ThirdPersonBP ``` `SampleProject/Config/DefaultEngine.ini`: ```ini [URL] GameName=SampleProject [/Script/EngineSettings.GameMapsSettings] EditorStartupMap=/Game/Sample/ThirdPersonBP/Maps/ThirdPersonExampleMap.ThirdPersonExampleMap GameDefaultMap=/Game/Sample/ThirdPersonBP/Maps/ThirdPersonExampleMap.ThirdPersonExampleMap TransitionMap= bUseSplitscreen=True TwoPlayerSplitscreenLayout=Horizontal ThreePlayerSplitscreenLayout=FavorTop GlobalDefaultGameMode=/Game/ThirdPersonBP/Blueprints/ThirdPersonGameMode.ThirdPersonGameMode_C GlobalDefaultServerGameMode=None bOffsetPlayerGamepadIds=True [/Script/IOSRuntimeSettings.IOSRuntimeSettings] MinimumiOSVersion=IOS_12 [/Script/HardwareTargeting.HardwareTargetingSettings] TargetedHardwareClass=Desktop AppliedTargetedHardwareClass=Desktop DefaultGraphicsPerformance=Maximum AppliedDefaultGraphicsPerformance=Maximum [/Script/Engine.Engine] +ActiveGameNameRedirects=(OldGameName="TP_ThirdPersonBP",NewGameName="/Script/SampleProject") +ActiveGameNameRedirects=(OldGameName="/Script/TP_ThirdPersonBP",NewGameName="/Script/SampleProject") [/Script/AndroidFileServerEditor.AndroidFileServerRuntimeSettings] bEnablePlugin=True bAllowNetworkConnection=True SecurityToken=55F6B26D4741D45F1FA58E99A86B603C bIncludeInShipping=False bAllowExternalStartInShipping=False bCompileAFSProject=False bUseCompression=False bLogFiles=False bReportStats=False ConnectionType=USBOnly bUseManualIPAddress=False ManualIPAddress= ``` `SampleProject/Config/DefaultGame.ini`: ```ini [/Script/EngineSettings.GeneralProjectSettings] ProjectID=6B549E774F9F66E4A73D9D8FD5F6C446 ProjectName=GameDebugMenu SampleProject [StartupActions] bAddPacks=True InsertPack=(PackSource="StarterContent.upack",PackName="StarterContent") [/Script/Engine.AssetManagerSettings] -PrimaryAssetTypesToScan=(PrimaryAssetType="Map",AssetBaseClass=/Script/Engine.World,bHasBlueprintClasses=False,bIsEditorOnly=True,Directories=((Path="/Game/Maps")),SpecificAssets=,Rules=(Priority=-1,ChunkId=-1,bApplyRecursively=True,CookRule=Unknown)) -PrimaryAssetTypesToScan=(PrimaryAssetType="PrimaryAssetLabel",AssetBaseClass=/Script/Engine.PrimaryAssetLabel,bHasBlueprintClasses=False,bIsEditorOnly=True,Directories=((Path="/Game")),SpecificAssets=,Rules=(Priority=-1,ChunkId=-1,bApplyRecursively=True,CookRule=Unknown)) +PrimaryAssetTypesToScan=(PrimaryAssetType="Map",AssetBaseClass="/Script/Engine.World",bHasBlueprintClasses=False,bIsEditorOnly=True,Directories=((Path="/Game/Maps")),SpecificAssets=,Rules=(Priority=-1,ChunkId=-1,bApplyRecursively=True,CookRule=Unknown)) +PrimaryAssetTypesToScan=(PrimaryAssetType="PrimaryAssetLabel",AssetBaseClass="/Script/Engine.PrimaryAssetLabel",bHasBlueprintClasses=False,bIsEditorOnly=True,Directories=((Path="/Game")),SpecificAssets=,Rules=(Priority=-1,ChunkId=-1,bApplyRecursively=True,CookRule=Unknown)) +PrimaryAssetTypesToScan=(PrimaryAssetType="GameDebugMenuMaster",AssetBaseClass="/Script/GameDebugMenu.GameDebugMenuMasterAsset",bHasBlueprintClasses=False,bIsEditorOnly=False,Directories=((Path="/Game/Sample/Debug")),SpecificAssets=,Rules=(Priority=-1,ChunkId=-1,bApplyRecursively=True,CookRule=DevelopmentAlwaysProductionNeverCook)) bOnlyCookProductionAssets=False bShouldManagerDetermineTypeAndName=False bShouldGuessTypeAndNameInEditor=True bShouldAcquireMissingChunksOnLoad=False bShouldWarnAboutInvalidAssets=True MetaDataTagsForAssetRegistry=() [/Script/UnrealEd.ProjectPackagingSettings] Build=IfProjectHasCode BuildConfiguration=PPBC_Development BuildTarget= FullRebuild=True ForDistribution=False IncludeDebugFiles=False BlueprintNativizationMethod=Disabled bIncludeNativizedAssetsInProjectGeneration=False bExcludeMonolithicEngineHeadersInNativizedCode=False UsePakFile=True bUseIoStore=True bUseZenStore=False bMakeBinaryConfig=False bGenerateChunks=False bGenerateNoChunks=False bChunkHardReferencesOnly=False bForceOneChunkPerFile=False MaxChunkSize=0 bBuildHttpChunkInstallData=False HttpChunkInstallDataDirectory=(Path="") WriteBackMetadataToAssetRegistry=Disabled bWritePluginSizeSummaryJsons=False bCompressed=True PackageCompressionFormat=Oodle bForceUseProjectCompressionFormatIgnoreHardwareOverride=False PackageAdditionalCompressionOptions= PackageCompressionMethod=Kraken PackageCompressionLevel_DebugDevelopment=4 PackageCompressionLevel_TestShipping=4 PackageCompressionLevel_Distribution=7 PackageCompressionMinBytesSaved=1024 PackageCompressionMinPercentSaved=5 bPackageCompressionEnableDDC=False PackageCompressionMinSizeToConsiderDDC=0 HttpChunkInstallDataVersion= IncludePrerequisites=True IncludeAppLocalPrerequisites=False bShareMaterialShaderCode=True bDeterministicShaderCodeOrder=False bSharedMaterialNativeLibraries=True ApplocalPrerequisitesDirectory=(Path="") IncludeCrashReporter=False InternationalizationPreset=English -CulturesToStage=en +CulturesToStage=en LocalizationTargetCatchAllChunkId=0 bCookAll=False bCookMapsOnly=False bTreatWarningsAsErrorsOnCook=False bSkipEditorContent=False bSkipMovies=False -IniKeyDenylist=KeyStorePassword -IniKeyDenylist=KeyPassword -IniKeyDenylist=DebugKeyStorePassword -IniKeyDenylist=DebugKeyPassword -IniKeyDenylist=rsa.privateexp -IniKeyDenylist=rsa.modulus -IniKeyDenylist=rsa.publicexp -IniKeyDenylist=aes.key -IniKeyDenylist=SigningPublicExponent -IniKeyDenylist=SigningModulus -IniKeyDenylist=SigningPrivateExponent -IniKeyDenylist=EncryptionKey -IniKeyDenylist=DevCenterUsername -IniKeyDenylist=DevCenterPassword -IniKeyDenylist=IOSTeamID -IniKeyDenylist=SigningCertificate -IniKeyDenylist=MobileProvision -IniKeyDenylist=AppStoreConnectKeyPath -IniKeyDenylist=AppStoreConnectIssuerID -IniKeyDenylist=AppStoreConnectKeyID -IniKeyDenylist=IniKeyDenylist -IniKeyDenylist=IniSectionDenylist +IniKeyDenylist=AppStoreConnectKeyPath +IniKeyDenylist=AppStoreConnectIssuerID +IniKeyDenylist=AppStoreConnectKeyID +IniKeyDenylist=KeyStorePassword +IniKeyDenylist=KeyPassword +IniKeyDenylist=DebugKeyStorePassword +IniKeyDenylist=DebugKeyPassword +IniKeyDenylist=rsa.privateexp +IniKeyDenylist=rsa.modulus +IniKeyDenylist=rsa.publicexp +IniKeyDenylist=aes.key +IniKeyDenylist=SigningPublicExponent +IniKeyDenylist=SigningModulus +IniKeyDenylist=SigningPrivateExponent +IniKeyDenylist=EncryptionKey +IniKeyDenylist=DevCenterUsername +IniKeyDenylist=DevCenterPassword +IniKeyDenylist=IOSTeamID +IniKeyDenylist=SigningCertificate +IniKeyDenylist=MobileProvision +IniKeyDenylist=IniKeyDenylist +IniKeyDenylist=IniSectionDenylist -IniSectionDenylist=HordeStorageServers -IniSectionDenylist=StorageServers -IniSectionDenylist=/Script/AndroidFileServerEditor.AndroidFileServerRuntimeSettings +IniSectionDenylist=HordeStorageServers +IniSectionDenylist=StorageServers +IniSectionDenylist=/Script/AndroidFileServerEditor.AndroidFileServerRuntimeSettings +DirectoriesToAlwaysCook=(Path="/NNEDenoiser") bRetainStagedDirectory=False CustomStageCopyHandler= ``` `SampleProject/Config/DefaultGameDebugMenu.ini`: ```ini [/Script/GameDebugMenu.GameDebugMenuSettings] +ConsoleCommandNames=(ConsoleCommandName="stat None",Type=Single,ClickedEvent=Non,CategoryIndex=0,Title="Stat None",Description="Statをクリアする",CommandNetType=LocalOnly) +ConsoleCommandNames=(ConsoleCommandName="stat Grouped",Type=Single,ClickedEvent=Non,CategoryIndex=0,Title="Grouped",Description="stat Slow を無効",CommandNetType=LocalOnly) +ConsoleCommandNames=(ConsoleCommandName="LoadTimes.reset",Type=Single,ClickedEvent=Non,CategoryIndex=0,Title="Load Times Report Reset",Description="loadtimes.dumpreportの出力情報をリセットする",CommandNetType=LocalOnly) +ConsoleCommandNames=(ConsoleCommandName="Slomo 1",Type=Single,ClickedEvent=Non,CategoryIndex=1,Title="Reset",Description="Slomoを1にリセットする",CommandNetType=ServerAll) +ConsoleCommandNames=(ConsoleCommandName="ToggleDebugCamera",Type=Single,ClickedEvent=MenuClose,CategoryIndex=2,Title="Debug Camera",Description="ゲーム中のカメラを離れ、デバッグ用の別カメラに切り替える\n同時に注視点のアセットの情報を画面に表示します",CommandNetType=LocalOnly) +ConsoleCommandNames=(ConsoleCommandName="Teleport",Type=Single,ClickedEvent=Non,CategoryIndex=2,Title="Teleport Player controlled pawn",Description="プレイヤー操作ポーンをカメラの注視点にテレポートさせる",CommandNetType=ServerAll) +ConsoleCommandNames=(ConsoleCommandName="stat fps",Type=Single,ClickedEvent=Non,CategoryIndex=3,Title="FPS",Description="",CommandNetType=LocalOnly) +ConsoleCommandNames=(ConsoleCommandName="stat Unit",Type=Single,ClickedEvent=Non,CategoryIndex=3,Title="Unit",Description="",CommandNetType=LocalOnly) +ConsoleCommandNames=(ConsoleCommandName="stat UnitGraph",Type=Single,ClickedEvent=Non,CategoryIndex=3,Title="Unit Graph",Description="",CommandNetType=LocalOnly) +ConsoleCommandNames=(ConsoleCommandName="stat UnitMax",Type=Single,ClickedEvent=Non,CategoryIndex=3,Title="Unit Max",Description="",CommandNetType=LocalOnly) +ConsoleCommandNames=(ConsoleCommandName="stat UnitTime",Type=Single,ClickedEvent=Non,CategoryIndex=3,Title="Unit Time",Description="",CommandNetType=LocalOnly) +ConsoleCommandNames=(ConsoleCommandName="stat Raw",Type=Single,ClickedEvent=Non,CategoryIndex=3,Title="Raw",Description="",CommandNetType=LocalOnly) +ConsoleCommandNames=(ConsoleCommandName="stat Game",Type=Single,ClickedEvent=Non,CategoryIndex=4,Title="Game",Description="各種 Gameplay のティックの所要時間に関するフィードバックです",CommandNetType=LocalOnly) +ConsoleCommandNames=(ConsoleCommandName="stat Engine",Type=Single,ClickedEvent=Non,CategoryIndex=4,Title="Engine",Description="フレーム時間やレンダリング中のトライアングル数のカウンタなど\n一般的なレンダリング統計情報を表示します",CommandNetType=LocalOnly) +ConsoleCommandNames=(ConsoleCommandName="stat InitViews",Type=Single,ClickedEvent=Non,CategoryIndex=4,Title="Init Views",Description="カリングの所要時間やプリミティブ数などの情報を表示する",CommandNetType=LocalOnly) +ConsoleCommandNames=(ConsoleCommandName="stat SceneRendering",Type=Single,ClickedEvent=Non,CategoryIndex=4,Title="Scene Rendering",Description="一般的なレンダリング統計を示す",CommandNetType=LocalOnly) +ConsoleCommandNames=(ConsoleCommandName="stat GPU",Type=Single,ClickedEvent=Non,CategoryIndex=4,Title="GPU",Description="ProfileGPUのリアルタイム版。GPUのフレーム単位の統計を表示する",CommandNetType=LocalOnly) +ConsoleCommandNames=(ConsoleCommandName="stat RHI",Type=Single,ClickedEvent=Non,CategoryIndex=4,Title="RHI",Description="全体のDrawCall数などを確認できる",CommandNetType=LocalOnly) +ConsoleCommandNames=(ConsoleCommandName="stat GPUParticles",Type=Single,ClickedEvent=Non,CategoryIndex=4,Title="Particles",Description="パーティクルの統計情報。今どのくらい出ていて、どのくらい処理がかかっているのかを表示する",CommandNetType=LocalOnly) +ConsoleCommandNames=(ConsoleCommandName="stat Levels",Type=Single,ClickedEvent=Non,CategoryIndex=4,Title="Levels",Description="現在読み込まれているLevelの表示。読み込みにかかった時間も表示される",CommandNetType=LocalOnly) +ConsoleCommandNames=(ConsoleCommandName="stat LightRendering",Type=Single,ClickedEvent=Non,CategoryIndex=4,Title="LightRendering",Description="ライティングとシャドウのレンダリングにかかる時間に関する情報を表示",CommandNetType=LocalOnly) +ConsoleCommandNames=(ConsoleCommandName="stat ShadowRendering",Type=Single,ClickedEvent=Non,CategoryIndex=4,Title="ShadowRendering",Description="stat LightRendering で示される実際のシャドウ レンダリング時間とは別に\nシャドウの計算にかかっている時間を表示",CommandNetType=LocalOnly) +ConsoleCommandNames=(ConsoleCommandName="stat Streaming",Type=Single,ClickedEvent=Non,CategoryIndex=4,Title="Streaming",Description="テクスチャのストリーミング処理で使用しているメモリ量、シーン内に存在するストリーミング中のテクスチャ数など\nストリーミング中のアセットの各種統計情報を表示",CommandNetType=LocalOnly) +ConsoleCommandNames=(ConsoleCommandName="stat StreamingDetails",Type=Single,ClickedEvent=Non,CategoryIndex=4,Title="Streaming Details",Description="一般的なテクスチャ ストリーミングをさらに特定のグループ\n (ライトマップ、静的 テクスチャ、動的テクスチャ) に分類するなど\nストリーミングに関するより詳しい統計情報を表示",CommandNetType=LocalOnly) +ConsoleCommandNames=(ConsoleCommandName="stat Memory",Type=Single,ClickedEvent=Non,CategoryIndex=4,Title="Memory",Description="メモリ使用量の統計を表示する",CommandNetType=LocalOnly) +ConsoleCommandNames=(ConsoleCommandName="stat Anim",Type=Single,ClickedEvent=Non,CategoryIndex=4,Title="Animation",Description="SkeletalMeshなどのティックごとの計算時間を表示する",CommandNetType=LocalOnly) +ConsoleCommandNames=(ConsoleCommandName="stat Slate",Type=Single,ClickedEvent=Non,CategoryIndex=4,Title="Slate",Description="Slateで使用するTickなどを表示する",CommandNetType=LocalOnly) +ConsoleCommandNames=(ConsoleCommandName="stat hitches",Type=Single,ClickedEvent=Non,CategoryIndex=4,Title="Hitches",Description="ヒッチを検知してログに出力する",CommandNetType=LocalOnly) +ConsoleCommandNames=(ConsoleCommandName="stat character",Type=Single,ClickedEvent=Non,CategoryIndex=4,Title="Character",Description="",CommandNetType=LocalOnly) +ConsoleCommandNames=(ConsoleCommandName="stat GameplayTags",Type=Single,ClickedEvent=Non,CategoryIndex=4,Title="GameplayTags",Description="",CommandNetType=LocalOnly) +ConsoleCommandNames=(ConsoleCommandName="stat Collision",Type=Single,ClickedEvent=Non,CategoryIndex=4,Title="Collision",Description="",CommandNetType=LocalOnly) +ConsoleCommandNames=(ConsoleCommandName="stat component",Type=Single,ClickedEvent=Non,CategoryIndex=4,Title="Component",Description="コンポーネントのパフォーマンス情報(Transform更新など)リストを表示する",CommandNetType=LocalOnly) +ConsoleCommandNames=(ConsoleCommandName="stat llm",Type=Single,ClickedEvent=Non,CategoryIndex=4,Title="LLM",Description="Low Level Memory Tracker(LLM)はメモリの使用状況を追跡するツールを使用し\nTag付けされた使用中のメモリを表示\n(LLM有効にはならないので事前に設定すること)",CommandNetType=LocalOnly) +ConsoleCommandNames=(ConsoleCommandName="stat LLMPlatform",Type=Single,ClickedEvent=Non,CategoryIndex=4,Title="LLMPlatform",Description="OSの情報を含むメモリ情報を表示",CommandNetType=LocalOnly) +ConsoleCommandNames=(ConsoleCommandName="stat UObjects",Type=Single,ClickedEvent=Non,CategoryIndex=4,Title="UObjects",Description="ゲーム内の UObjects のパフォーマンスに関する統計情報を表示",CommandNetType=LocalOnly) +ConsoleCommandNames=(ConsoleCommandName="ShowDebug",Type=Single,ClickedEvent=Non,CategoryIndex=5,Title="Toggle ShowDebug",Description="HUDを使用したデバック情報を画面に表示するかを切り替える",CommandNetType=LocalOnly) +ConsoleCommandNames=(ConsoleCommandName="NextDebugTarget",Type=Single,ClickedEvent=Non,CategoryIndex=5,Title="ShowDebug NextDebugTarget",Description="デバック情報の表示対象アクターを切り替える(次へ進む)",CommandNetType=LocalOnly) +ConsoleCommandNames=(ConsoleCommandName="PreviousDebugTarget",Type=Single,ClickedEvent=Non,CategoryIndex=5,Title="ShowDebug PreviousDebugTarget",Description="デバック情報の表示対象アクターを切り替える(戻る)",CommandNetType=LocalOnly) +ConsoleCommandNames=(ConsoleCommandName="ShowDebug Reset",Type=Single,ClickedEvent=Non,CategoryIndex=5,Title="ShowDebug Reset",Description="HUDを使用したデバック情報の表示項目をリセットする",CommandNetType=LocalOnly) +ConsoleCommandNames=(ConsoleCommandName="ShowDebug Input",Type=Single,ClickedEvent=Non,CategoryIndex=5,Title="ShowDebug Input",Description="入力情報を表示する",CommandNetType=LocalOnly) +ConsoleCommandNames=(ConsoleCommandName="Showdebug Enhancedinput",Type=Single,ClickedEvent=Non,CategoryIndex=5,Title="ShowDebug Enhancedinput",Description="Enhancedinputの入力情報を表示する",CommandNetType=LocalOnly) +ConsoleCommandNames=(ConsoleCommandName="ShowDebug COLLISION",Type=Single,ClickedEvent=Non,CategoryIndex=5,Title="ShowDebug Collision",Description="対象アクターのコリジョン情報を表示する",CommandNetType=LocalOnly) +ConsoleCommandNames=(ConsoleCommandName="ShowDebug PHYSICS",Type=Single,ClickedEvent=Non,CategoryIndex=5,Title="ShowDebug Physics",Description="対象アクターのPhysics関係の情報を表示する",CommandNetType=LocalOnly) +ConsoleCommandNames=(ConsoleCommandName="ShowDebug FORCEFEEDBACK",Type=Single,ClickedEvent=Non,CategoryIndex=5,Title="ShowDebug Forcefeedback",Description="",CommandNetType=LocalOnly) +ConsoleCommandNames=(ConsoleCommandName="ShowDebug CAMERA",Type=Single,ClickedEvent=Non,CategoryIndex=5,Title="ShowDebug Camera",Description="アクティブなカメラの情報を表示する",CommandNetType=LocalOnly) +ConsoleCommandNames=(ConsoleCommandName="ShowDebug ANIMATION",Type=Single,ClickedEvent=Non,CategoryIndex=5,Title="ShowDebug Animation",Description="対象アクターのSkeletalMeshのアニメーション情報を表示する",CommandNetType=LocalOnly) +ConsoleCommandNames=(ConsoleCommandName="ShowDebugToggleSubCategory GRAPH",Type=Single,ClickedEvent=Non,CategoryIndex=5,Title="ShowDebug Animation Toggle Graph",Description="アニムグラフの内容を表示を切り替える",CommandNetType=LocalOnly) +ConsoleCommandNames=(ConsoleCommandName="ShowDebugToggleSubCategory FULLGRAPH",Type=Single,ClickedEvent=Non,CategoryIndex=5,Title="ShowDebug Animation Toggle FullGraph",Description="アニムグラフの内容をすべて表示するかを切り替える",CommandNetType=LocalOnly) +ConsoleCommandNames=(ConsoleCommandName="ShowDebugToggleSubCategory CURVES",Type=Single,ClickedEvent=Non,CategoryIndex=5,Title="ShowDebug Animation Toggle Curves",Description="Curve情報を表示するか切り替える",CommandNetType=LocalOnly) +ConsoleCommandNames=(ConsoleCommandName="ShowDebugToggleSubCategory MONTAGES",Type=Single,ClickedEvent=Non,CategoryIndex=5,Title="ShowDebug Animation Toggle Montages",Description="モンタージュ情報を表示するか切り替える",CommandNetType=LocalOnly) +ConsoleCommandNames=(ConsoleCommandName="ShowDebugToggleSubCategory NOTIFIES",Type=Single,ClickedEvent=Non,CategoryIndex=5,Title="ShowDebug Animation Toggle Notifies",Description="アニメーション通知情報を表示するか切り替える",CommandNetType=LocalOnly) +ConsoleCommandNames=(ConsoleCommandName="ShowDebugToggleSubCategory SYNCGROUPS",Type=Single,ClickedEvent=Non,CategoryIndex=5,Title="ShowDebug Animation Toggle SyncGroups",Description="SyncGroup情報を表示するか切り替える",CommandNetType=LocalOnly) +ConsoleCommandNames=(ConsoleCommandName="ShowDebugToggleSubCategory FULLBLENDSPACEDISPLAY",Type=Single,ClickedEvent=Non,CategoryIndex=5,Title="ShowDebug Animation Toggle FullBlendSpaceDisplay",Description="ブレンドスペースの重み値の表示を切り替える(SYNCGROUPSが表示されるとき反映)",CommandNetType=LocalOnly) +ConsoleCommandNames=(ConsoleCommandName="viewmode lit",Type=Single,ClickedEvent=Non,CategoryIndex=6,Title="Lit",Description="シーン全てのライティングがある状態\nエディターでかつStandaloneのみ有効\n(Package環境で使用したい場合は「r.ForceDebugViewModes」を1に)",CommandNetType=LocalOnly) +ConsoleCommandNames=(ConsoleCommandName="viewmode unlit",Type=Single,ClickedEvent=Non,CategoryIndex=6,Title="Unlit",Description="シーン全てのライティングを切った状態\nエディターでかつStandaloneのみ有効\n(Package環境で使用したい場合は「r.ForceDebugViewModes」を1に)",CommandNetType=LocalOnly) +ConsoleCommandNames=(ConsoleCommandName="viewmode Wireframe",Type=Single,ClickedEvent=Non,CategoryIndex=6,Title="Wireframe",Description="シーン内のすべてのポリゴンエッジを表示",CommandNetType=LocalOnly) +ConsoleCommandNames=(ConsoleCommandName="viewmode lit_detaillighting",Type=Single,ClickedEvent=Non,CategoryIndex=6,Title="Detaillighting",Description="シーン全体に中間色のマテリアルをアクティベートする\n基本色が暗すぎたり、ライティングが不明瞭になっている場合の分離に有効\nエディターでかつStandaloneのみ有効\n(Package環境で使用したい場合は「r.ForceDebugViewModes」を1に)",CommandNetType=LocalOnly) +ConsoleCommandNames=(ConsoleCommandName="viewmode lightingonly",Type=Single,ClickedEvent=Non,CategoryIndex=6,Title="Lighting Only",Description="ライティングにのみ影響を受ける中間色のマテリアルを表示\nエディターでかつStandaloneのみ有効\n(Package環境で使用したい場合は「r.ForceDebugViewModes」を1に)",CommandNetType=LocalOnly) +ConsoleCommandNames=(ConsoleCommandName="viewmode ReflectionOverride",Type=Single,ClickedEvent=Non,CategoryIndex=6,Title="Reflection",Description="反射の表示\nエディターでかつStandaloneのみ有効\n(Package環境で使用したい場合は「r.ForceDebugViewModes」を1に)",CommandNetType=LocalOnly) +ConsoleCommandNames=(ConsoleCommandName="viewmode lightcomplexity",Type=Single,ClickedEvent=Non,CategoryIndex=6,Title="Light Complexity",Description="ジオメトリに影響を与える非静的ライトの数を表示\nライトがサーフェスに影響を与えるほど、描画負荷が高くなる\nエディターでかつStandaloneのみ有効\n(Package環境で使用したい場合は「r.ForceDebugViewModes」を1に)",CommandNetType=LocalOnly) +ConsoleCommandNames=(ConsoleCommandName="viewmode LightMapDensity",Type=Single,ClickedEvent=Non,CategoryIndex=6,Title="Lightmap Density",Description="テクスチャ マッピングされるオブジェクトのライトマップ密度を表示\n理想的/最大限の密度設定別に色分けして、実際のライトマップ テクセルへマッピングするグリッドを表示\nエディターでかつStandaloneのみ有効\n(Package環境で使用したい場合は「r.ForceDebugViewModes」を1に)",CommandNetType=LocalOnly) +ConsoleCommandNames=(ConsoleCommandName="viewmode shadercomplexity",Type=Single,ClickedEvent=Non,CategoryIndex=6,Title="Shader Complexity",Description="シーンの各ピクセルの計算に使用しているシェーダ命令数を視覚化する\nエディターでかつStandaloneのみ有効\n(Package環境で使用したい場合は「r.ForceDebugViewModes」を1に)",CommandNetType=LocalOnly) +ConsoleCommandNames=(ConsoleCommandName="viewmode ShaderComplexityWithQuadOverdraw",Type=Single,ClickedEvent=Non,CategoryIndex=6,Title="ShaderComplexity With Quadoverdraw",Description="エディターでかつStandaloneのみ有効\n(Package環境で使用したい場合は「r.ForceDebugViewModes」を1に)",CommandNetType=LocalOnly) +ConsoleCommandNames=(ConsoleCommandName="viewmode Quadoverdraw",Type=Single,ClickedEvent=Non,CategoryIndex=6,Title="Quadoverdraw",Description="エディターでかつStandaloneのみ有効\n(Package環境で使用したい場合は「r.ForceDebugViewModes」を1に)",CommandNetType=LocalOnly) +ConsoleCommandNames=(ConsoleCommandName="show collision",Type=Single,ClickedEvent=Non,CategoryIndex=6,Title="Collision",Description="コリジョン(非)表示",CommandNetType=LocalOnly) +ConsoleCommandNames=(ConsoleCommandName="show CollisionPawn",Type=Single,ClickedEvent=Non,CategoryIndex=6,Title="Collision Pawn",Description="",CommandNetType=LocalOnly) +ConsoleCommandNames=(ConsoleCommandName="show CollisionVisibility",Type=Single,ClickedEvent=Non,CategoryIndex=6,Title="Collision Visibility",Description="",CommandNetType=LocalOnly) +ConsoleCommandNames=(ConsoleCommandName="show bounds",Type=Single,ClickedEvent=Non,CategoryIndex=6,Title="Bounds",Description="各Actorのバウンディングボックスの表示",CommandNetType=LocalOnly) +ConsoleCommandNames=(ConsoleCommandName="show ShadowFrustums",Type=Single,ClickedEvent=Non,CategoryIndex=6,Title="Shadow Frustums",Description="動的な影を生成しているフラスタムを表示",CommandNetType=LocalOnly) +ConsoleCommandNames=(ConsoleCommandName="show DynamicShadows",Type=Single,ClickedEvent=Non,CategoryIndex=6,Title="Dynamic Shadows",Description="全ての動的シャドウを切り替え (シャドウマップ レンダリングとシャドウ フィルタリング / プロジェクション)",CommandNetType=LocalOnly) +ConsoleCommandNames=(ConsoleCommandName="show Volumes",Type=Single,ClickedEvent=Non,CategoryIndex=6,Title="Volumes",Description="AVolume継承アクターのボリュームを(非)表示",CommandNetType=LocalOnly) +ConsoleCommandNames=(ConsoleCommandName="show Navigation",Type=Single,ClickedEvent=Non,CategoryIndex=6,Title="Navigation",Description="ナビメッシュの(非)表示",CommandNetType=LocalOnly) +ConsoleCommandNames=(ConsoleCommandName="show DeferredLighting",Type=Single,ClickedEvent=Non,CategoryIndex=6,Title="Deferred Lighting",Description="すべてのディファード ライティング パスを切り替え",CommandNetType=LocalOnly) +ConsoleCommandNames=(ConsoleCommandName="show DirectionalLights",Type=Single,ClickedEvent=Non,CategoryIndex=6,Title="DirectionalLights",Description="ディレクショナルライトの表示切り替え",CommandNetType=LocalOnly) +ConsoleCommandNames=(ConsoleCommandName="show PointLights",Type=Single,ClickedEvent=Non,CategoryIndex=6,Title="PointLights",Description="ポイントライトの表示切り替え",CommandNetType=LocalOnly) +ConsoleCommandNames=(ConsoleCommandName="show SpotLights",Type=Single,ClickedEvent=Non,CategoryIndex=6,Title="SpotLights",Description="スポットライトの表示切り替え",CommandNetType=LocalOnly) +ConsoleCommandNames=(ConsoleCommandName="show Rendering",Type=Single,ClickedEvent=Non,CategoryIndex=6,Title="Rendering",Description="",CommandNetType=LocalOnly) +ConsoleCommandNames=(ConsoleCommandName="show SkyLighting",Type=Single,ClickedEvent=Non,CategoryIndex=6,Title="SkyLighting",Description="",CommandNetType=LocalOnly) +ConsoleCommandNames=(ConsoleCommandName="show Decals",Type=Single,ClickedEvent=Non,CategoryIndex=6,Title="Decals",Description="デカールの表示切り替え",CommandNetType=LocalOnly) +ConsoleCommandNames=(ConsoleCommandName="show PostProcessing",Type=Single,ClickedEvent=Non,CategoryIndex=6,Title="Post Processing",Description="",CommandNetType=LocalOnly) +ConsoleCommandNames=(ConsoleCommandName="show Particles",Type=Single,ClickedEvent=Non,CategoryIndex=6,Title="Particles",Description="",CommandNetType=LocalOnly) +ConsoleCommandNames=(ConsoleCommandName="show Specular",Type=Single,ClickedEvent=Non,CategoryIndex=6,Title="Specular",Description="",CommandNetType=LocalOnly) +ConsoleCommandNames=(ConsoleCommandName="show Translucency",Type=Single,ClickedEvent=Non,CategoryIndex=6,Title="Translucency",Description="",CommandNetType=LocalOnly) +ConsoleCommandNames=(ConsoleCommandName="show Diffuse",Type=Single,ClickedEvent=Non,CategoryIndex=6,Title="Diffuse",Description="",CommandNetType=LocalOnly) +ConsoleCommandNames=(ConsoleCommandName="show ReflectionEnvironment",Type=Single,ClickedEvent=Non,CategoryIndex=6,Title="ReflectionEnvironment",Description="",CommandNetType=LocalOnly) +ConsoleCommandNames=(ConsoleCommandName="show Refraction",Type=Single,ClickedEvent=Non,CategoryIndex=6,Title="Refraction",Description="",CommandNetType=LocalOnly) +ConsoleCommandNames=(ConsoleCommandName="show AmbientOcclusion",Type=Single,ClickedEvent=Non,CategoryIndex=6,Title="AmbientOcclusion",Description="",CommandNetType=LocalOnly) +ConsoleCommandNames=(ConsoleCommandName="show GlobalIllumination",Type=Single,ClickedEvent=Non,CategoryIndex=6,Title="GlobalIllumination",Description="ベイクされた、動的な間接ライティングを切り替え",CommandNetType=LocalOnly) +ConsoleCommandNames=(ConsoleCommandName="show LightFunctions",Type=Single,ClickedEvent=Non,CategoryIndex=6,Title="LightFunctions",Description="ライト関数のレンダリングを切り替え",CommandNetType=LocalOnly) +ConsoleCommandNames=(ConsoleCommandName="show DepthOfField",Type=Single,ClickedEvent=Non,CategoryIndex=6,Title="DepthOfField",Description="",CommandNetType=LocalOnly) +ConsoleCommandNames=(ConsoleCommandName="show AntiAliasing",Type=Single,ClickedEvent=Non,CategoryIndex=6,Title="AntiAliasing",Description="",CommandNetType=LocalOnly) +ConsoleCommandNames=(ConsoleCommandName="show Bloom",Type=Single,ClickedEvent=Non,CategoryIndex=6,Title="Bloom",Description="",CommandNetType=LocalOnly) +ConsoleCommandNames=(ConsoleCommandName="show StaticMeshes",Type=Single,ClickedEvent=Non,CategoryIndex=6,Title="StaticMeshes",Description="",CommandNetType=LocalOnly) +ConsoleCommandNames=(ConsoleCommandName="show SkeletalMeshes",Type=Single,ClickedEvent=Non,CategoryIndex=6,Title="SkeletalMeshes",Description="",CommandNetType=LocalOnly) +ConsoleCommandNames=(ConsoleCommandName="show Landscape",Type=Single,ClickedEvent=Non,CategoryIndex=6,Title="Landscape",Description="",CommandNetType=LocalOnly) +ConsoleCommandNames=(ConsoleCommandName="show Tessellation",Type=Single,ClickedEvent=Non,CategoryIndex=6,Title="Tessellation",Description="テッセレーションを切り替え (テッセレーション シェーダーは実行されたまま)",CommandNetType=LocalOnly) +ConsoleCommandNames=(ConsoleCommandName="show VisualizeVolumetricLightmap",Type=Single,ClickedEvent=Non,CategoryIndex=6,Title="VisualizeVolumetricLightmap",Description="ボリュームライトマップの可視化",CommandNetType=LocalOnly) +ConsoleCommandNames=(ConsoleCommandName="show ScreenSpaceReflections",Type=Single,ClickedEvent=Non,CategoryIndex=6,Title="ScreenSpaceReflections",Description="スクリーン スペースの反射を切り替え",CommandNetType=LocalOnly) +ConsoleCommandNames=(ConsoleCommandName="show ShaderComplexity",Type=Single,ClickedEvent=Non,CategoryIndex=6,Title="Shader Complexity",Description="シーンの各ピクセルを計算するために使用されているシェーダ命令の数を視覚化",CommandNetType=LocalOnly) +ConsoleCommandNames=(ConsoleCommandName="FreezeRendering",Type=Single,ClickedEvent=Non,CategoryIndex=8,Title="Freeze Rendering",Description="レベル内にあるオクルードされたアクタ\nおよび表示されているアクタの現在のレンダリングの状態を一時停止 / 解除する",CommandNetType=LocalOnly) +ConsoleCommandNames=(ConsoleCommandName="FX.FreezeParticleSimulation",Type=Single,ClickedEvent=Non,CategoryIndex=8,Title="FreezeParticleSimulation",Description="レベル内のすべての CPU スプライト パーティクル シミュレーション\nの一時停止 / 解除する",CommandNetType=LocalOnly) +ConsoleCommandNames=(ConsoleCommandName="FX.FreezeGPUSimulation",Type=Single,ClickedEvent=Non,CategoryIndex=8,Title="FreezeGPUSimulation",Description="レベル内のすべての GPU スプライト パーティクル シミュレーション\nの一時停止 / 解除する",CommandNetType=LocalOnly) +ConsoleCommandNames=(ConsoleCommandName="Obj List",Type=Single,ClickedEvent=Non,CategoryIndex=9,Title="Obj List",Description="OutputLogウィンドウに各オブジェクトの数と使用メモリ量を出力する",CommandNetType=LocalOnly) +ConsoleCommandNames=(ConsoleCommandName="Obj List Forget",Type=Single,ClickedEvent=Non,CategoryIndex=9,Title="Obj List Forget",Description="Obj Listで取得したObjectを記憶し\n これ以降のObj Listでは差分が表示されるようになる",CommandNetType=LocalOnly) +ConsoleCommandNames=(ConsoleCommandName="Obj List Remember",Type=Single,ClickedEvent=Non,CategoryIndex=9,Title="Obj List Remember",Description="Obj List Forget で記憶したオブジェクトをリセットする",CommandNetType=LocalOnly) +ConsoleCommandNames=(ConsoleCommandName="memreport",Type=Single,ClickedEvent=Non,CategoryIndex=9,Title="MemReport",Description="YourGame/Saved/Profiling/MemReports以下にメモリ使用ログを出力する",CommandNetType=LocalOnly) +ConsoleCommandNames=(ConsoleCommandName="memreport -full",Type=Single,ClickedEvent=Non,CategoryIndex=9,Title="MemReport Full",Description="MemReportのより詳細にログを出力します",CommandNetType=LocalOnly) +ConsoleCommandNames=(ConsoleCommandName="dumpticks Enable",Type=Single,ClickedEvent=Non,CategoryIndex=9,Title="Dump Ticks",Description="Level上に存在するActorやComponentのTickが有効なものをリストアップしログ表示する",CommandNetType=LocalOnly) +ConsoleCommandNames=(ConsoleCommandName="loadtimes.dumpreport",Type=Single,ClickedEvent=Non,CategoryIndex=9,Title="Loadtimes Dumpreport",Description="ロードされたファイルのロード時間を降順でログ出力する\n(loadtimes.resetでタイミング調整可能)",CommandNetType=LocalOnly) +ConsoleCommandNames=(ConsoleCommandName="stat DumpHitches",Type=Single,ClickedEvent=Non,CategoryIndex=9,Title="Dump Hitches",Description="t.HitchFrameTimeThreshold に基づいて処理落ちが検出されるたびに\nログへ書き込まれる",CommandNetType=LocalOnly) +ConsoleCommandNames=(ConsoleCommandName="log LogGarbage log",Type=Single,ClickedEvent=Non,CategoryIndex=9,Title="Log Garbage",Description="GCが起きたときの検索、削除コストをログに表示する",CommandNetType=LocalOnly) +ConsoleCommandNames=(ConsoleCommandName="log list",Type=Single,ClickedEvent=Non,CategoryIndex=9,Title="Log List",Description="各ログカテゴリの一覧と、各カテゴリの現在の設定を確認できる",CommandNetType=LocalOnly) +ConsoleCommandNames=(ConsoleCommandName="showdebug abilitysystem",Type=Single,ClickedEvent=Non,CategoryIndex=12,Title="Show abilitysystem",Description="AbilitySystemのデバック表示をする",CommandNetType=LocalOnly) +ConsoleCommandNames=(ConsoleCommandName="showdebug",Type=Single,ClickedEvent=Non,CategoryIndex=12,Title="Close abilitysystem",Description="AbilitySystemの非表示にする",CommandNetType=LocalOnly) +ConsoleCommandNames=(ConsoleCommandName="abilitySystem.debug.nextCategory",Type=Single,ClickedEvent=Non,CategoryIndex=12,Title="AbilitySystem NextCategory",Description="AbilitySystemのデバック表示がされてる場合表示内容(所持アビリティ、エフェクトなど)を切り替える",CommandNetType=LocalOnly) +ConsoleCommandNames=(ConsoleCommandName="NextDebugTarget",Type=Single,ClickedEvent=Non,CategoryIndex=12,Title="AbilitySystem NextTarget",Description="AbilitySystemのデバック表示対象を変更する(次へ移る)",CommandNetType=LocalOnly) +ConsoleCommandNames=(ConsoleCommandName="PreviousDebugTarget",Type=Single,ClickedEvent=Non,CategoryIndex=12,Title="AbilitySystem PrevTarget",Description="AbilitySystemのデバック表示を対象を変更する(1つ戻る)",CommandNetType=LocalOnly) +ConsoleCommandNames=(ConsoleCommandName="AbilitySystem.DebugBasicHUD",Type=Single,ClickedEvent=Non,CategoryIndex=12,Title="AbilitySystem DebugBasicHUD",Description="操作キャラのAttribute情報を表示",CommandNetType=LocalOnly) +ConsoleCommandNames=(ConsoleCommandName="AbilitySystem.DebugAbilityTags",Type=Single,ClickedEvent=Non,CategoryIndex=12,Title="AbilitySystem DebugAbilityTags",Description="AbilitySystemで所持するタグ情報をアクター位置にすべて表示",CommandNetType=LocalOnly) +ConsoleCommandNames=(ConsoleCommandName="AbilitySystem.DebugAttribute",Type=Single,ClickedEvent=Non,CategoryIndex=12,Title="AbilitySystem DebugAttribute",Description="AbilitySystemで所持するAttribute情報をアクター位置にすべて表示",CommandNetType=LocalOnly) +ConsoleCommandNames=(ConsoleCommandName="AbilitySystem.ClearDebugAttributes",Type=Single,ClickedEvent=Non,CategoryIndex=12,Title="AbilitySystem ClearDebugAttributes",Description="AbilitySystemで所持するAttribute情報をクリアする",CommandNetType=LocalOnly) +ConsoleCommandNames=(ConsoleCommandName="RestartLevel",Type=Single,ClickedEvent=Non,CategoryIndex=13,Title="RestartLevel",Description="レベルの再読込み",CommandNetType=LocalOnly) +ConsoleCommandNames=(ConsoleCommandName="ShowLog",Type=Single,ClickedEvent=Non,CategoryIndex=13,Title="ShowLog",Description="コンソールウィンドウの(非)表示する",CommandNetType=LocalOnly) +ConsoleCommandNames=(ConsoleCommandName="Obj trygc",Type=Single,ClickedEvent=Non,CategoryIndex=13,Title="Try Garbage Collection",Description="ガーベジコレクションを即時実行し、定期実行タイマーをリセットする",CommandNetType=LocalOnly) +ConsoleCommandNames=(ConsoleCommandName="exit",Type=Single,ClickedEvent=Non,CategoryIndex=13,Title="Exit Game",Description="ゲームを即終了させる",CommandNetType=LocalOnly) +ConsoleCommandNames=(ConsoleCommandName="log LogTemp Display",Type=Single,ClickedEvent=Non,CategoryIndex=14,Title="LogTemp - Display",Description="LogTempのVerbosityを「Display」に変更",CommandNetType=LocalOnly) +ConsoleCommandNames=(ConsoleCommandName="log LogTemp Log",Type=Single,ClickedEvent=Non,CategoryIndex=14,Title="LogTemp - Log",Description="LogTempのVerbosityを「Log」に変更",CommandNetType=LocalOnly) +ConsoleCommandNames=(ConsoleCommandName="log LogTemp Verbose",Type=Single,ClickedEvent=Non,CategoryIndex=14,Title="LogTemp - Verbose",Description="LogTempのVerbosityを「Verbose」に変更",CommandNetType=LocalOnly) +ConsoleCommandNames=(ConsoleCommandName="log LogTemp VeryVerbose",Type=Single,ClickedEvent=Non,CategoryIndex=14,Title="LogTemp - VeryVerbose",Description="LogTempのVerbosityを「VeryVerbose」に変更",CommandNetType=LocalOnly) +ConsoleCommandNames=(ConsoleCommandName="log LogAbilitySystem Display",Type=Single,ClickedEvent=Non,CategoryIndex=14,Title="LogAbilitySystem - Display",Description="LogAbilitySystemのVerbosityを「Display」に変更",CommandNetType=LocalOnly) +ConsoleCommandNames=(ConsoleCommandName="log LogAbilitySystem Log",Type=Single,ClickedEvent=Non,CategoryIndex=14,Title="LogAbilitySystem - Log",Description="LogAbilitySystemのVerbosityを「Log」に変更",CommandNetType=LocalOnly) +ConsoleCommandNames=(ConsoleCommandName="log LogAbilitySystem Verbose",Type=Single,ClickedEvent=Non,CategoryIndex=14,Title="LogAbilitySystem - Verbose",Description="LogAbilitySystemのVerbosityを「Verbose」に変更",CommandNetType=LocalOnly) +ConsoleCommandNames=(ConsoleCommandName="log LogAbilitySystem VeryVerbose",Type=Single,ClickedEvent=Non,CategoryIndex=14,Title="LogAbilitySystem - VeryVerbose",Description="LogAbilitySystemのVerbosityを「VeryVerbose」に変更",CommandNetType=LocalOnly) +ConsoleCommandGroups=(ConsoleCommandNames=("r.ProfileGPU.ShowUI 0","ProfileGPU"),Type=Group,ClickedEvent=Non,CategoryIndex=3,Title="ProfileGPU",Description="GPUの処理負荷を階層的に出力",CommandNetType=LocalOnly) +ConsoleCommandGroups=(ConsoleCommandNames=("stat AI","stat AIBehaviorTree","stat AI_EQS","stat Navigation"),Type=Group,ClickedEvent=Non,CategoryIndex=4,Title="AI Info",Description="AIに必要な情報(BehaviorTree,EQS)をまとめて表示する",CommandNetType=LocalOnly) +ConsoleCommandGroups=(ConsoleCommandNames=("stat MemoryPlatform","stat MemoryStaticMesh","stat MemoryAllocator"),Type=Group,ClickedEvent=Non,CategoryIndex=4,Title="All Memory Info",Description="使用量、他アロケーター情報などを一括表示する",CommandNetType=LocalOnly) +ConsoleCommandGroups=(ConsoleCommandNames=("stat SoundMixes","stat SoundWaves","stat SoundCues","stat Sounds"),Type=Group,ClickedEvent=Non,CategoryIndex=11,Title="Sounds",Description="アクティブなサウンドキューとサウンドウェーブ、SoundMixeを表示",CommandNetType=LocalOnly) +ConsoleCommandPairs=(FirstConsoleCommandName="DisableAllScreenMessages",SecondConsoleCommandName="EnableAllScreenMessages",Type=Pair,ClickedEvent=Non,CategoryIndex=0,Title="Toggle All Screen Messages",Description="トグルで画面ログの(非)表示を切り替える",CommandNetType=LocalOnly) +ConsoleCommandPairs=(FirstConsoleCommandName="g.DebugCameraTraceComplex 0",SecondConsoleCommandName="g.DebugCameraTraceComplex 1",Type=Pair,ClickedEvent=Non,CategoryIndex=2,Title="Debug Camera Trace Complex",Description="",CommandNetType=LocalOnly) +ConsoleCommandPairs=(FirstConsoleCommandName="r.RHISetGPUCaptureOptions 1",SecondConsoleCommandName="r.RHISetGPUCaptureOptions 0",Type=Pair,ClickedEvent=Non,CategoryIndex=3,Title="Toggle RHISetGPUCaptureOptions",Description="BasePass内部の各DrawCall毎の処理負荷を出力する\n(マテリアル名などが見られるようになったり) ※RHIThreadがOffになり、DrawThread負荷が増加する点に注意",CommandNetType=LocalOnly) +ConsoleCommandPairs=(FirstConsoleCommandName="stat startfile",SecondConsoleCommandName="stat stopfile",Type=Pair,ClickedEvent=MenuClose,CategoryIndex=3,Title="Toggle Profile",Description="startfile / stopfile をトグルで実行。プロファイル結果をファイルにue4stats形式で出力する",CommandNetType=LocalOnly) +ConsoleCommandPairs=(FirstConsoleCommandName="CsvProfile Start",SecondConsoleCommandName="CsvProfile Stop",Type=Pair,ClickedEvent=MenuClose,CategoryIndex=3,Title="Toggle CSV Profiler",Description="CsvProfile Start / Stop のトグル",CommandNetType=LocalOnly) +ConsoleCommandPairs=(FirstConsoleCommandName="p.VisualizeMovement 1",SecondConsoleCommandName="p.VisualizeMovement 0",Type=Pair,ClickedEvent=Non,CategoryIndex=5,Title="Toggle VisualizeMovement",Description="キャラクターの加速度や移動モードを表示させる",CommandNetType=LocalOnly) +ConsoleCommandPairs=(FirstConsoleCommandName="a.VisualizeLODs 1",SecondConsoleCommandName="a.VisualizeLODs 0",Type=Pair,ClickedEvent=Non,CategoryIndex=5,Title="Toggle VisualizeLODs",Description="SkinnedMeshComponentで適応してるLODの確認\nボーン数、頂点数などが確認可能(Shipping / Test以外のビルドで有効)\nLOD0:White LOD1:Green LOD2:Yellow LOD3:Red Other:Purple",CommandNetType=LocalOnly) +ConsoleCommandPairs=(FirstConsoleCommandName="r.VisualizeOccludedPrimitives 1",SecondConsoleCommandName="r.VisualizeOccludedPrimitives 0",Type=Pair,ClickedEvent=Non,CategoryIndex=5,Title="Toggle VisualizeOccludedPrimitives",Description="オクルードされたアクタを視覚化する",CommandNetType=LocalOnly) +ConsoleCommandPairs=(FirstConsoleCommandName="ShowFlag.LODColoration 1",SecondConsoleCommandName="ShowFlag.LODColoration 0",Type=Pair,ClickedEvent=Non,CategoryIndex=5,Title="Toggle LODColoration",Description="PrimitiveComponentのLODレベル毎の色で確認\nLOD0:White LOD1:Red LOD2:Green LOD3:Blue LOD4:Yellow LOD5:Fuchisia LOD6:Cyan LOD7:Purple",CommandNetType=LocalOnly) +ConsoleCommandPairs=(FirstConsoleCommandName="r.VSync 0",SecondConsoleCommandName="r.VSync 1",Type=Pair,ClickedEvent=Non,CategoryIndex=7,Title="Toggle VSync",Description="垂直同期の切り替え",CommandNetType=LocalOnly) +ConsoleCommandPairs=(FirstConsoleCommandName="Foliage.Freeze",SecondConsoleCommandName="Foliage.Unfreeze",Type=Pair,ClickedEvent=Non,CategoryIndex=8,Title="Foliage Freeze / Unfreeze",Description="レベル内でオクルードおよび表示された\nペイントされたフォリッジ クラスタの現在のレンダリングの状態を一時停止 / 解除する",CommandNetType=LocalOnly) +ConsoleCommandNumbers=(ConsoleCommandName="Slomo",PreConsoleCommandName="",PostConsoleCommandName=" ",UIConfigInfo=(Range=(bUseMin=True,bUseMax=True,MinValue=0.000000,MaxValue=10.000000),DefaultChangeAmount=0.100000,MaxChangeAmount=0.200000,MaxChangeAmountTime=1.300000),DefaultValue=1.000000,ConsoleVariableName="Slomo",Type=Number,ClickedEvent=Non,CategoryIndex=1,Title="Change Slomo",Description="Slomoを変更し反映させる",CommandNetType=ServerAll) +ConsoleCommandNumbers=(ConsoleCommandName="ViewDistanceQuality",PreConsoleCommandName="sg.",PostConsoleCommandName=" ",UIConfigInfo=(Range=(bUseMin=True,bUseMax=True,MinValue=0.000000,MaxValue=3.000000),DefaultChangeAmount=1.000000,MaxChangeAmount=1.000000,MaxChangeAmountTime=1.300000),DefaultValue=0.000000,ConsoleVariableName="sg.ViewDistanceQuality",Type=Number,ClickedEvent=Non,CategoryIndex=7,Title="ViewDistanceQuality",Description="",CommandNetType=LocalOnly) +ConsoleCommandNumbers=(ConsoleCommandName="AntiAliasingQuality",PreConsoleCommandName="sg.",PostConsoleCommandName=" ",UIConfigInfo=(Range=(bUseMin=True,bUseMax=True,MinValue=0.000000,MaxValue=6.000000),DefaultChangeAmount=1.000000,MaxChangeAmount=1.000000,MaxChangeAmountTime=1.300000),DefaultValue=0.000000,ConsoleVariableName="sg.AntiAliasingQuality",Type=Number,ClickedEvent=Non,CategoryIndex=7,Title="AntiAliasingQuality",Description="",CommandNetType=LocalOnly) +ConsoleCommandNumbers=(ConsoleCommandName="PostProcessQuality",PreConsoleCommandName="sg.",PostConsoleCommandName=" ",UIConfigInfo=(Range=(bUseMin=True,bUseMax=True,MinValue=0.000000,MaxValue=3.000000),DefaultChangeAmount=1.000000,MaxChangeAmount=1.000000,MaxChangeAmountTime=1.300000),DefaultValue=0.000000,ConsoleVariableName="sg.PostProcessQuality",Type=Number,ClickedEvent=Non,CategoryIndex=7,Title="PostProcessQuality",Description="",CommandNetType=LocalOnly) +ConsoleCommandNumbers=(ConsoleCommandName="ShadowQuality",PreConsoleCommandName="sg.",PostConsoleCommandName=" ",UIConfigInfo=(Range=(bUseMin=True,bUseMax=True,MinValue=0.000000,MaxValue=3.000000),DefaultChangeAmount=1.000000,MaxChangeAmount=1.000000,MaxChangeAmountTime=1.300000),DefaultValue=0.000000,ConsoleVariableName="sg.ShadowQuality",Type=Number,ClickedEvent=Non,CategoryIndex=7,Title="ShadowQuality",Description="",CommandNetType=LocalOnly) +ConsoleCommandNumbers=(ConsoleCommandName="TextureQuality",PreConsoleCommandName="sg.",PostConsoleCommandName=" ",UIConfigInfo=(Range=(bUseMin=True,bUseMax=True,MinValue=0.000000,MaxValue=3.000000),DefaultChangeAmount=1.000000,MaxChangeAmount=1.000000,MaxChangeAmountTime=1.300000),DefaultValue=0.000000,ConsoleVariableName="sg.TextureQuality",Type=Number,ClickedEvent=Non,CategoryIndex=7,Title="TextureQuality",Description="",CommandNetType=LocalOnly) +ConsoleCommandNumbers=(ConsoleCommandName="EffectsQuality",PreConsoleCommandName="sg.",PostConsoleCommandName=" ",UIConfigInfo=(Range=(bUseMin=True,bUseMax=True,MinValue=0.000000,MaxValue=3.000000),DefaultChangeAmount=1.000000,MaxChangeAmount=1.000000,MaxChangeAmountTime=1.300000),DefaultValue=0.000000,ConsoleVariableName="sg.EffectsQuality",Type=Number,ClickedEvent=Non,CategoryIndex=7,Title="EffectsQuality",Description="",CommandNetType=LocalOnly) +ConsoleCommandNumbers=(ConsoleCommandName="FoliageQuality",PreConsoleCommandName="sg.",PostConsoleCommandName=" ",UIConfigInfo=(Range=(bUseMin=True,bUseMax=True,MinValue=0.000000,MaxValue=3.000000),DefaultChangeAmount=1.000000,MaxChangeAmount=1.000000,MaxChangeAmountTime=1.300000),DefaultValue=0.000000,ConsoleVariableName="sg.FoliageQuality",Type=Number,ClickedEvent=Non,CategoryIndex=7,Title="FoliageQuality",Description="",CommandNetType=LocalOnly) +ConsoleCommandNumbers=(ConsoleCommandName="ShadingQuality",PreConsoleCommandName="sg.",PostConsoleCommandName=" ",UIConfigInfo=(Range=(bUseMin=True,bUseMax=True,MinValue=0.000000,MaxValue=3.000000),DefaultChangeAmount=1.000000,MaxChangeAmount=1.000000,MaxChangeAmountTime=1.300000),DefaultValue=0.000000,ConsoleVariableName="sg.ShadingQuality",Type=Number,ClickedEvent=Non,CategoryIndex=7,Title="ShadingQuality",Description="",CommandNetType=LocalOnly) +ConsoleCommandNumbers=(ConsoleCommandName="ScreenPercentage",PreConsoleCommandName="r.",PostConsoleCommandName=" ",UIConfigInfo=(Range=(bUseMin=True,bUseMax=True,MinValue=10.000000,MaxValue=200.000000),DefaultChangeAmount=1.000000,MaxChangeAmount=5.000000,MaxChangeAmountTime=1.300000),DefaultValue=0.000000,ConsoleVariableName="r.ScreenPercentage",Type=Number,ClickedEvent=Non,CategoryIndex=7,Title="ScreenPercentage",Description="",CommandNetType=LocalOnly) +ConsoleCommandNumbers=(ConsoleCommandName="DynamicRes.OperationMode",PreConsoleCommandName="r.",PostConsoleCommandName=" ",UIConfigInfo=(Range=(bUseMin=True,bUseMax=True,MinValue=0.000000,MaxValue=2.000000),DefaultChangeAmount=1.000000,MaxChangeAmount=1.000000,MaxChangeAmountTime=1.300000),DefaultValue=0.000000,ConsoleVariableName="r.DynamicRes.OperationMode",Type=Number,ClickedEvent=Non,CategoryIndex=7,Title="DynamicRes.OperationMode",Description="動的解像度の使用方法の切り替え\n 0 = 無効\n 1 = GameUserSettingsの設定に基づいて有効化\n2 = GameUserSettingsを考慮せず有効化",CommandNetType=LocalOnly) +ConsoleCommandNumbers=(ConsoleCommandName="MaterialQualityLevel",PreConsoleCommandName="r.",PostConsoleCommandName=" ",UIConfigInfo=(Range=(bUseMin=True,bUseMax=True,MinValue=0.000000,MaxValue=2.000000),DefaultChangeAmount=1.000000,MaxChangeAmount=1.000000,MaxChangeAmountTime=1.300000),DefaultValue=0.000000,ConsoleVariableName="r.MaterialQualityLevel",Type=Number,ClickedEvent=Non,CategoryIndex=7,Title="MaterialQualityLevel",Description="マテリアル品質レベルの変更\n0 = Low: 1 = High: 2 = Medium",CommandNetType=LocalOnly) +ConsoleCommandNumbers=(ConsoleCommandName="Kernel.MaxBackgroundRadius",PreConsoleCommandName="r.DOF.",PostConsoleCommandName=" ",UIConfigInfo=(Range=(bUseMin=True,bUseMax=True,MinValue=0.000000,MaxValue=1.000000),DefaultChangeAmount=0.001000,MaxChangeAmount=0.005000,MaxChangeAmountTime=1.300000),DefaultValue=0.000000,ConsoleVariableName="r.DOF.Kernel.MaxBackgroundRadius",Type=Number,ClickedEvent=Non,CategoryIndex=7,Title="DOF - MaxBackgroundRadius",Description="水平スクリーン空間のバックグラウンド ブラー半径の最大サイズ",CommandNetType=LocalOnly) +ConsoleCommandNumbers=(ConsoleCommandName="Kernel.MaxForegroundRadius",PreConsoleCommandName="r.DOF.",PostConsoleCommandName=" ",UIConfigInfo=(Range=(bUseMin=True,bUseMax=True,MinValue=0.000000,MaxValue=1.000000),DefaultChangeAmount=0.001000,MaxChangeAmount=0.005000,MaxChangeAmountTime=1.300000),DefaultValue=0.000000,ConsoleVariableName="r.DOF.Kernel.MaxForegroundRadius",Type=Number,ClickedEvent=Non,CategoryIndex=7,Title="DOF - MaxForegroundRadius",Description="水平スクリーン空間のフォアグラウンド ブラー半径の最大サイズ",CommandNetType=LocalOnly) +ConsoleCommandNumbers=(ConsoleCommandName="Scatter.MaxSpriteRatio",PreConsoleCommandName="r.DOF.",PostConsoleCommandName=" ",UIConfigInfo=(Range=(bUseMin=True,bUseMax=True,MinValue=0.000000,MaxValue=1.000000),DefaultChangeAmount=0.010000,MaxChangeAmount=0.050000,MaxChangeAmountTime=1.300000),DefaultValue=0.000000,ConsoleVariableName="r.DOF.Scatter.MaxSpriteRatio",Type=Number,ClickedEvent=Non,CategoryIndex=7,Title="DOF - MaxSpriteRatio",Description="スプライトとしての散乱ピクセル クワッドの最大比率\nDOF の散乱の上限をコントロールするために便利\n1では100%のピクセルクワッドを散乱可能に",CommandNetType=LocalOnly) +ConsoleCommandNumbers=(ConsoleCommandName="HitchFrameTimeThreshold",PreConsoleCommandName="t.",PostConsoleCommandName=" ",UIConfigInfo=(Range=(bUseMin=True,bUseMax=False,MinValue=10.000000,MaxValue=1.000000),DefaultChangeAmount=5.000000,MaxChangeAmount=10.000000,MaxChangeAmountTime=1.300000),DefaultValue=60.000000,ConsoleVariableName="t.HitchFrameTimeThreshold",Type=Number,ClickedEvent=Non,CategoryIndex=9,Title="Hitch Frame Time Threshold",Description="DumpHitches実行時ログに出力するかどうかのしきい値(ミリ秒)\n この値よりも処理に時間がかかるものがログに出力されるようになる",CommandNetType=LocalOnly) +ConsoleCommandNumbers=(ConsoleCommandName="PktLag",PreConsoleCommandName="NetEmulation.",PostConsoleCommandName=" ",UIConfigInfo=(Range=(bUseMin=True,bUseMax=False,MinValue=0.000000,MaxValue=1.000000),DefaultChangeAmount=10.000000,MaxChangeAmount=100.000000,MaxChangeAmountTime=1.300000),DefaultValue=0.000000,ConsoleVariableName="",Type=Number,ClickedEvent=Non,CategoryIndex=10,Title="PktLag",Description="パケット送信を遅らせる(ミリ秒)",CommandNetType=ServerAll) +ConsoleCommandNumbers=(ConsoleCommandName="PktLagVariance",PreConsoleCommandName="NetEmulation.",PostConsoleCommandName=" ",UIConfigInfo=(Range=(bUseMin=True,bUseMax=False,MinValue=0.000000,MaxValue=1.000000),DefaultChangeAmount=10.000000,MaxChangeAmount=100.000000,MaxChangeAmountTime=1.300000),DefaultValue=0.000000,ConsoleVariableName="",Type=Number,ClickedEvent=Non,CategoryIndex=10,Title="PktLagVariance",Description="パケット送信を指定範囲内でランダムに遅らせる(±ミリ秒)",CommandNetType=ServerAll) +ConsoleCommandNumbers=(ConsoleCommandName="PktLoss",PreConsoleCommandName="NetEmulation.",PostConsoleCommandName=" ",UIConfigInfo=(Range=(bUseMin=True,bUseMax=True,MinValue=0.000000,MaxValue=100.000000),DefaultChangeAmount=1.000000,MaxChangeAmount=10.000000,MaxChangeAmountTime=1.300000),DefaultValue=0.000000,ConsoleVariableName="",Type=Number,ClickedEvent=Non,CategoryIndex=10,Title="PktLoss",Description="一定の確率でパケットを送信しないようにする(0~100%)",CommandNetType=ServerAll) +ConsoleCommandNumbers=(ConsoleCommandName="PktOrder",PreConsoleCommandName="NetEmulation.",PostConsoleCommandName=" ",UIConfigInfo=(Range=(bUseMin=True,bUseMax=True,MinValue=0.000000,MaxValue=1.000000),DefaultChangeAmount=1.000000,MaxChangeAmount=1.000000,MaxChangeAmountTime=1.300000),DefaultValue=0.000000,ConsoleVariableName="",Type=Number,ClickedEvent=Non,CategoryIndex=10,Title="PktOrder",Description="パケットをバッファリングして送信順序をランダムにする(1=有効、0=無効)",CommandNetType=ServerAll) +ConsoleCommandNumbers=(ConsoleCommandName="PktDup",PreConsoleCommandName="NetEmulation.",PostConsoleCommandName=" ",UIConfigInfo=(Range=(bUseMin=True,bUseMax=True,MinValue=0.000000,MaxValue=100.000000),DefaultChangeAmount=1.000000,MaxChangeAmount=10.000000,MaxChangeAmountTime=1.300000),DefaultValue=0.000000,ConsoleVariableName="",Type=Number,ClickedEvent=Non,CategoryIndex=10,Title="PktDup",Description="パケット送信時に指定確率で重複送信する(0~100%)",CommandNetType=ServerAll) +ConsoleCommandNumbers=(ConsoleCommandName="RPC",PreConsoleCommandName="net.",PostConsoleCommandName=".Debug ",UIConfigInfo=(Range=(bUseMin=True,bUseMax=True,MinValue=0.000000,MaxValue=1.000000),DefaultChangeAmount=1.000000,MaxChangeAmount=1.000000,MaxChangeAmountTime=1.300000),DefaultValue=0.000000,ConsoleVariableName="net.RPC.Debug",Type=Number,ClickedEvent=Non,CategoryIndex=10,Title="RPC Debug",Description="すべてのRPC Bunchをログ表示(1=有効、0=無効)",CommandNetType=LocalOnly) +ConsoleCommandNumbers=(ConsoleCommandName="Reliable",PreConsoleCommandName="net.",PostConsoleCommandName=".Debug ",UIConfigInfo=(Range=(bUseMin=True,bUseMax=True,MinValue=0.000000,MaxValue=2.000000),DefaultChangeAmount=1.000000,MaxChangeAmount=1.000000,MaxChangeAmountTime=1.300000),DefaultValue=0.000000,ConsoleVariableName="net.Reliable.Debug",Type=Number,ClickedEvent=Non,CategoryIndex=10,Title="Reliable Debug",Description="すべてのReliable Bunchのログ表示(0=出力なし、1=送信時のみ出力、2=更新時に出力)",CommandNetType=LocalOnly) +ConsoleCommandNumbers=(ConsoleCommandName="NetShowCorrections",PreConsoleCommandName="p.",PostConsoleCommandName=" ",UIConfigInfo=(Range=(bUseMin=True,bUseMax=True,MinValue=0.000000,MaxValue=1.000000),DefaultChangeAmount=1.000000,MaxChangeAmount=1.000000,MaxChangeAmountTime=1.300000),DefaultValue=0.000000,ConsoleVariableName="p.NetShowCorrections",Type=Number,ClickedEvent=Non,CategoryIndex=10,Title="NetShowCorrections",Description="クライアント (またはサーバーへ送信) がネットワーク修正を受け取った時期を確認できるようにする(1=有効、0=無効)",CommandNetType=LocalOnly) +ConsoleCommandNumbers=(ConsoleCommandName="DebugDraw",PreConsoleCommandName="net.",PostConsoleCommandName=" ",UIConfigInfo=(Range=(bUseMin=True,bUseMax=True,MinValue=0.000000,MaxValue=1.000000),DefaultChangeAmount=1.000000,MaxChangeAmount=1.000000,MaxChangeAmountTime=1.300000),DefaultValue=0.000000,ConsoleVariableName="net.DebugDraw",Type=Number,ClickedEvent=Non,CategoryIndex=10,Title="Debug Draw",Description="ネットワークのdormancyとrelevancyの情報を表示(1=有効、0=無効)",CommandNetType=LocalOnly) +ConsoleCommandNumbers=(ConsoleCommandName="ContextDebug",PreConsoleCommandName="net.",PostConsoleCommandName=" ",UIConfigInfo=(Range=(bUseMin=True,bUseMax=True,MinValue=0.000000,MaxValue=1.000000),DefaultChangeAmount=1.000000,MaxChangeAmount=1.000000,MaxChangeAmountTime=1.300000),DefaultValue=0.000000,ConsoleVariableName="net.ContextDebug",Type=Number,ClickedEvent=Non,CategoryIndex=10,Title="Context Debug",Description="Replication情報にデバッグ用文字列を設定してログに詳細な情報を表示(1=有効、0=無効)",CommandNetType=LocalOnly) +ConsoleCommandNumbers=(ConsoleCommandName="DormancyEnable",PreConsoleCommandName="net.",PostConsoleCommandName=" ",UIConfigInfo=(Range=(bUseMin=True,bUseMax=True,MinValue=0.000000,MaxValue=1.000000),DefaultChangeAmount=1.000000,MaxChangeAmount=1.000000,MaxChangeAmountTime=1.300000),DefaultValue=1.000000,ConsoleVariableName="net.DormancyEnable",Type=Number,ClickedEvent=Non,CategoryIndex=10,Title="Dormancy Enable",Description="頻繁に更新されるアクターのCPUおよび帯域幅のオーバーヘッドを削減するネットワーク休止システムの有効化(1=有効、0=無効)",CommandNetType=LocalOnly) +ConsoleCommandNumbers=(ConsoleCommandName="DebugDrawCullDistance",PreConsoleCommandName="net.",PostConsoleCommandName=" ",UIConfigInfo=(Range=(bUseMin=True,bUseMax=False,MinValue=0.000000,MaxValue=0.000000),DefaultChangeAmount=50.000000,MaxChangeAmount=100.000000,MaxChangeAmountTime=1.300000),DefaultValue=0.000000,ConsoleVariableName="net.DebugDrawCullDistance",Type=Number,ClickedEvent=Non,CategoryIndex=10,Title="Debug Draw CullDistance",Description="localのViewから離れているActorの休止状態を描画する\nこの距離(World単位)より近いものが描画対象(net.DebugDrawが有効かつ 0 以上指定で動作)",CommandNetType=LocalOnly) +ConsoleCommandNumbers=(ConsoleCommandName="AbilitySystem.DebugDrawMaxDistance",PreConsoleCommandName="",PostConsoleCommandName=" ",UIConfigInfo=(Range=(bUseMin=True,bUseMax=False,MinValue=10.000000,MaxValue=0.000000),DefaultChangeAmount=10.000000,MaxChangeAmount=100.000000,MaxChangeAmountTime=1.300000),DefaultValue=2048.000000,ConsoleVariableName="AbilitySystem.DebugDrawMaxDistance",Type=Number,ClickedEvent=Non,CategoryIndex=12,Title="AbilitySystem.DebugDrawMaxDistance",Description="アクター位置に表示するAbilitySystemのデバック情報の表示距離を設定する",CommandNetType=LocalOnly) +ConsoleCommandNumbers=(ConsoleCommandName="MaxFPS",PreConsoleCommandName="t.",PostConsoleCommandName=" ",UIConfigInfo=(Range=(bUseMin=True,bUseMax=True,MinValue=15.000000,MaxValue=200.000000),DefaultChangeAmount=5.000000,MaxChangeAmount=10.000000,MaxChangeAmountTime=1.300000),DefaultValue=60.000000,ConsoleVariableName="t.MaxFPS",Type=Number,ClickedEvent=Non,CategoryIndex=13,Title="Change Max FPS",Description="FPSの変更",CommandNetType=LocalOnly) +EditorOnlyConsoleCommandNames=(ConsoleCommandName="show StationaryLightOverlap",Type=Single,ClickedEvent=Non,CategoryIndex=6,Title="Stationary Light Overlap",Description="Stationary Lightの重なりを視覚化する\nエディターでのみ有効",CommandNetType=LocalOnly) +EditorOnlyConsoleCommandNames=(ConsoleCommandName="show VisualizeBuffer",Type=Single,ClickedEvent=Non,CategoryIndex=6,Title="Visualize Buffer",Description="各レンダーバッファの可視化\nエディターでのみ有効",CommandNetType=LocalOnly) +EditorOnlyConsoleCommandNames=(ConsoleCommandName="show Materials",Type=Single,ClickedEvent=Non,CategoryIndex=6,Title="Materials",Description="マテリアルのオンオフ\nエディターでのみ有効",CommandNetType=LocalOnly) +EditorOnlyConsoleCommandGroups=(ConsoleCommandNames=("r.ProfileGPU.ShowUI 1","ProfileGPU"),Type=Group,ClickedEvent=Non,CategoryIndex=3,Title="ProfileGPU ShowUI",Description="GPUの処理負荷を階層的に出力(エディター用UI表示版)\nエディターでのみ有効",CommandNetType=LocalOnly) +OrderConsoleCommandCategoryTitles=(Title="Resets",Index=0,PreviewTitle="") +OrderConsoleCommandCategoryTitles=(Title="Slomo",Index=1,PreviewTitle="") +OrderConsoleCommandCategoryTitles=(Title="Camera",Index=2,PreviewTitle="") +OrderConsoleCommandCategoryTitles=(Title="Profiler",Index=3,PreviewTitle="") +OrderConsoleCommandCategoryTitles=(Title="Display",Index=4,PreviewTitle="") +OrderConsoleCommandCategoryTitles=(Title="Show Debug",Index=5,PreviewTitle="") +OrderConsoleCommandCategoryTitles=(Title="ViewMode",Index=6,PreviewTitle="") +OrderConsoleCommandCategoryTitles=(Title="Scalability",Index=7,PreviewTitle="") +OrderConsoleCommandCategoryTitles=(Title="Freeze",Index=8,PreviewTitle="") +OrderConsoleCommandCategoryTitles=(Title="Dump Logs",Index=9,PreviewTitle="") +OrderConsoleCommandCategoryTitles=(Title="Network",Index=10,PreviewTitle="") +OrderConsoleCommandCategoryTitles=(Title="Sounds",Index=11,PreviewTitle="") +OrderConsoleCommandCategoryTitles=(Title="Ability System",Index=12,PreviewTitle="") +OrderConsoleCommandCategoryTitles=(Title="Other",Index=13,PreviewTitle="") +OrderConsoleCommandCategoryTitles=(Title="Log Verbosity",Index=14,PreviewTitle="") +OrderGameplayCategoryTitles=(Title="Other",Index=0,PreviewTitle="") +OrderGameplayCategoryTitles=(Title="TestA",Index=1,PreviewTitle="") +OrderGameplayCategoryTitles=(Title="TestB",Index=2,PreviewTitle="") WidgetInputActionPriority=2147483646 ProjectManagementToolType=Trello RedmineSettings=(HostName="localhost",ProjectId=1,TrackerNameList=("バグ","機能","サポート"),DefaultTrackerIndex=0,PriorityNameList=("低め","通常","高め","急いで","今すぐ"),DefaultPriorityIndex=1,AccessKey="") TrelloSettings=(AccessToken="",CardListIDs=,CardListNames=("Trello追加先リスト名"),AccessKey="") JiraSettings=(HostName="localhost",ProjectKeyName="",UserName="",IssueTypeList=("タスク","バグ","改善"),DefaultIssueTypeIndex=1,PriorityNameList=("Highest","High","Medium","Low","Lowest"),DefaultPriorityIndex=2,AssigneeList=(),LabelNameList=,AccessKey="") +CultureList=ja +CultureList=en DefaultGameDebugMenuLanguage=Japanese bUseSaveGame=False SaveFilePath=Saved/DebugMenu SaveFileName=DebugMenuSaveData bDisableSaveFile=False bDoesNotSaveConsoleCommand=False MaxCommandHistoryNum=100 +NoSaveConsoleCommands=LevelEditor. +NoSaveConsoleCommands=ToggleDebugCamera +NoSaveConsoleCommands="stat " +NoSaveConsoleCommands=LoadTimes. +NoSaveConsoleCommands="CsvProfile " +NoSaveConsoleCommands="Obj " +NoSaveConsoleCommands=Freeze LineBreakString= bDisableGameDebugMenu=False DebugMenuInputComponentClass=None DebugReportRequesterClass=() GameDebugMenuStringTables=() FontName=None MasterAssetName=DA_SampleGDM_Master ``` `SampleProject/Config/DefaultInput.ini`: ```ini [/Script/Engine.InputSettings] -AxisConfig=(AxisKeyName="Gamepad_LeftX",AxisProperties=(DeadZone=0.25,Exponent=1.f,Sensitivity=1.f)) -AxisConfig=(AxisKeyName="Gamepad_LeftY",AxisProperties=(DeadZone=0.25,Exponent=1.f,Sensitivity=1.f)) -AxisConfig=(AxisKeyName="Gamepad_RightX",AxisProperties=(DeadZone=0.25,Exponent=1.f,Sensitivity=1.f)) -AxisConfig=(AxisKeyName="Gamepad_RightY",AxisProperties=(DeadZone=0.25,Exponent=1.f,Sensitivity=1.f)) -AxisConfig=(AxisKeyName="MouseX",AxisProperties=(DeadZone=0.f,Exponent=1.f,Sensitivity=0.07f)) -AxisConfig=(AxisKeyName="MouseY",AxisProperties=(DeadZone=0.f,Exponent=1.f,Sensitivity=0.07f)) -AxisConfig=(AxisKeyName="Mouse2D",AxisProperties=(DeadZone=0.f,Exponent=1.f,Sensitivity=0.07f)) +AxisConfig=(AxisKeyName="ValveIndex_Right_Trigger_Axis",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False)) +AxisConfig=(AxisKeyName="ValveIndex_Right_Thumbstick_X",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False)) +AxisConfig=(AxisKeyName="ValveIndex_Right_Thumbstick_Y",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False)) +AxisConfig=(AxisKeyName="ValveIndex_Right_Trackpad_X",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False)) +AxisConfig=(AxisKeyName="ValveIndex_Right_Trackpad_Y",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False)) +AxisConfig=(AxisKeyName="ValveIndex_Right_Trackpad_Force",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False)) +AxisConfig=(AxisKeyName="Mouse2D",AxisProperties=(DeadZone=0.000000,Sensitivity=0.070000,Exponent=1.000000,bInvert=False)) +AxisConfig=(AxisKeyName="Gamepad_LeftX",AxisProperties=(DeadZone=0.250000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False)) +AxisConfig=(AxisKeyName="Gamepad_LeftY",AxisProperties=(DeadZone=0.250000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False)) +AxisConfig=(AxisKeyName="Gamepad_RightX",AxisProperties=(DeadZone=0.250000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False)) +AxisConfig=(AxisKeyName="Gamepad_RightY",AxisProperties=(DeadZone=0.250000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False)) +AxisConfig=(AxisKeyName="MouseX",AxisProperties=(DeadZone=0.000000,Sensitivity=0.070000,Exponent=1.000000,bInvert=False)) +AxisConfig=(AxisKeyName="MouseY",AxisProperties=(DeadZone=0.000000,Sensitivity=0.070000,Exponent=1.000000,bInvert=False)) +AxisConfig=(AxisKeyName="MouseWheelAxis",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False)) +AxisConfig=(AxisKeyName="Gamepad_LeftTriggerAxis",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False)) +AxisConfig=(AxisKeyName="Gamepad_RightTriggerAxis",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False)) +AxisConfig=(AxisKeyName="MotionController_Left_Thumbstick_X",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False)) +AxisConfig=(AxisKeyName="MotionController_Left_Thumbstick_Y",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False)) +AxisConfig=(AxisKeyName="MotionController_Left_TriggerAxis",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False)) +AxisConfig=(AxisKeyName="MotionController_Left_Grip1Axis",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False)) +AxisConfig=(AxisKeyName="MotionController_Left_Grip2Axis",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False)) +AxisConfig=(AxisKeyName="MotionController_Right_Thumbstick_X",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False)) +AxisConfig=(AxisKeyName="MotionController_Right_Thumbstick_Y",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False)) +AxisConfig=(AxisKeyName="MotionController_Right_TriggerAxis",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False)) +AxisConfig=(AxisKeyName="MotionController_Right_Grip1Axis",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False)) +AxisConfig=(AxisKeyName="MotionController_Right_Grip2Axis",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False)) +AxisConfig=(AxisKeyName="Gamepad_Special_Left_X",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False)) +AxisConfig=(AxisKeyName="Gamepad_Special_Left_Y",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False)) +AxisConfig=(AxisKeyName="MotionController_Left_Thumbstick_Z",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False)) +AxisConfig=(AxisKeyName="MotionController_Right_Thumbstick_Z",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False)) +AxisConfig=(AxisKeyName="OculusTouch_Left_Thumbstick",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False)) +AxisConfig=(AxisKeyName="OculusTouch_Left_FaceButton1",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False)) +AxisConfig=(AxisKeyName="OculusTouch_Left_Trigger",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False)) +AxisConfig=(AxisKeyName="OculusTouch_Left_FaceButton2",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False)) +AxisConfig=(AxisKeyName="OculusTouch_Left_IndexPointing",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False)) +AxisConfig=(AxisKeyName="OculusTouch_Left_ThumbUp",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False)) +AxisConfig=(AxisKeyName="OculusTouch_Right_Thumbstick",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False)) +AxisConfig=(AxisKeyName="OculusTouch_Right_FaceButton1",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False)) +AxisConfig=(AxisKeyName="OculusTouch_Right_Trigger",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False)) +AxisConfig=(AxisKeyName="OculusTouch_Right_FaceButton2",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False)) +AxisConfig=(AxisKeyName="OculusTouch_Right_IndexPointing",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False)) +AxisConfig=(AxisKeyName="OculusTouch_Right_ThumbUp",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False)) +AxisConfig=(AxisKeyName="OculusTouchpad_Touchpad_X",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False)) +AxisConfig=(AxisKeyName="OculusTouchpad_Touchpad_Y",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False)) +AxisConfig=(AxisKeyName="SteamVR_Knuckles_Left_HandGrip",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False)) +AxisConfig=(AxisKeyName="SteamVR_Knuckles_Left_IndexGrip",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False)) +AxisConfig=(AxisKeyName="SteamVR_Knuckles_Left_MiddleGrip",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False)) +AxisConfig=(AxisKeyName="SteamVR_Knuckles_Left_RingGrip",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False)) +AxisConfig=(AxisKeyName="SteamVR_Knuckles_Left_PinkyGrip",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False)) +AxisConfig=(AxisKeyName="SteamVR_Knuckles_Right_HandGrip",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False)) +AxisConfig=(AxisKeyName="SteamVR_Knuckles_Right_IndexGrip",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False)) +AxisConfig=(AxisKeyName="SteamVR_Knuckles_Right_MiddleGrip",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False)) +AxisConfig=(AxisKeyName="SteamVR_Knuckles_Right_RingGrip",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False)) +AxisConfig=(AxisKeyName="SteamVR_Knuckles_Right_PinkyGrip",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False)) +AxisConfig=(AxisKeyName="Daydream_Left_Trackpad_X",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False)) +AxisConfig=(AxisKeyName="Daydream_Left_Trackpad_Y",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False)) +AxisConfig=(AxisKeyName="Daydream_Right_Trackpad_X",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False)) +AxisConfig=(AxisKeyName="Daydream_Right_Trackpad_Y",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False)) +AxisConfig=(AxisKeyName="Vive_Left_Trigger_Axis",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False)) +AxisConfig=(AxisKeyName="Vive_Left_Trackpad_X",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False)) +AxisConfig=(AxisKeyName="Vive_Left_Trackpad_Y",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False)) +AxisConfig=(AxisKeyName="Vive_Right_Trigger_Axis",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False)) +AxisConfig=(AxisKeyName="Vive_Right_Trackpad_X",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False)) +AxisConfig=(AxisKeyName="Vive_Right_Trackpad_Y",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False)) +AxisConfig=(AxisKeyName="MixedReality_Left_Trigger_Axis",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False)) +AxisConfig=(AxisKeyName="MixedReality_Left_Thumbstick_X",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False)) +AxisConfig=(AxisKeyName="MixedReality_Left_Thumbstick_Y",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False)) +AxisConfig=(AxisKeyName="MixedReality_Left_Trackpad_X",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False)) +AxisConfig=(AxisKeyName="MixedReality_Left_Trackpad_Y",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False)) +AxisConfig=(AxisKeyName="MixedReality_Right_Trigger_Axis",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False)) +AxisConfig=(AxisKeyName="MixedReality_Right_Thumbstick_X",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False)) +AxisConfig=(AxisKeyName="MixedReality_Right_Thumbstick_Y",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False)) +AxisConfig=(AxisKeyName="MixedReality_Right_Trackpad_X",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False)) +AxisConfig=(AxisKeyName="MixedReality_Right_Trackpad_Y",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False)) +AxisConfig=(AxisKeyName="OculusGo_Left_Trackpad_X",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False)) +AxisConfig=(AxisKeyName="OculusGo_Left_Trackpad_Y",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False)) +AxisConfig=(AxisKeyName="OculusGo_Right_Trackpad_X",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False)) +AxisConfig=(AxisKeyName="OculusGo_Right_Trackpad_Y",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False)) +AxisConfig=(AxisKeyName="OculusTouch_Left_Grip_Axis",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False)) +AxisConfig=(AxisKeyName="OculusTouch_Left_Trigger_Axis",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False)) +AxisConfig=(AxisKeyName="OculusTouch_Left_Thumbstick_X",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False)) +AxisConfig=(AxisKeyName="OculusTouch_Left_Thumbstick_Y",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False)) +AxisConfig=(AxisKeyName="OculusTouch_Right_Grip_Axis",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False)) +AxisConfig=(AxisKeyName="OculusTouch_Right_Trigger_Axis",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False)) +AxisConfig=(AxisKeyName="OculusTouch_Right_Thumbstick_X",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False)) +AxisConfig=(AxisKeyName="OculusTouch_Right_Thumbstick_Y",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False)) +AxisConfig=(AxisKeyName="ValveIndex_Left_Grip_Axis",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False)) +AxisConfig=(AxisKeyName="ValveIndex_Left_Grip_Force",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False)) +AxisConfig=(AxisKeyName="ValveIndex_Left_Trigger_Axis",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False)) +AxisConfig=(AxisKeyName="ValveIndex_Left_Thumbstick_X",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False)) +AxisConfig=(AxisKeyName="ValveIndex_Left_Thumbstick_Y",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False)) +AxisConfig=(AxisKeyName="ValveIndex_Left_Trackpad_X",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False)) +AxisConfig=(AxisKeyName="ValveIndex_Left_Trackpad_Y",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False)) +AxisConfig=(AxisKeyName="ValveIndex_Left_Trackpad_Force",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False)) +AxisConfig=(AxisKeyName="ValveIndex_Left_Trackpad_Touch",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False)) +AxisConfig=(AxisKeyName="ValveIndex_Right_Grip_Axis",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False)) +AxisConfig=(AxisKeyName="ValveIndex_Right_Grip_Force",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False)) bAltEnterTogglesFullscreen=True bF11TogglesFullscreen=True bUseMouseForTouch=False bEnableMouseSmoothing=True bEnableFOVScaling=True bCaptureMouseOnLaunch=True bEnableLegacyInputScales=True bEnableMotionControls=True bFilterInputByPlatformUser=False bShouldFlushPressedKeysOnViewportFocusLost=True bEnableDynamicComponentInputBinding=True bAlwaysShowTouchInterface=False bShowConsoleOnFourFingerTap=True bEnableGestureRecognizer=False bUseAutocorrect=False DefaultViewportMouseCaptureMode=CapturePermanently_IncludingInitialMouseDown DefaultViewportMouseLockMode=LockOnCapture FOVScale=0.011110 DoubleClickTime=0.200000 +ActionMappings=(ActionName="Jump",bShift=False,bCtrl=False,bAlt=False,bCmd=False,Key=SpaceBar) +ActionMappings=(ActionName="Jump",bShift=False,bCtrl=False,bAlt=False,bCmd=False,Key=Gamepad_FaceButton_Bottom) +ActionMappings=(ActionName="Jump",bShift=False,bCtrl=False,bAlt=False,bCmd=False,Key=Daydream_Left_Select_Click) +ActionMappings=(ActionName="ResetVR",bShift=False,bCtrl=False,bAlt=False,bCmd=False,Key=R) +ActionMappings=(ActionName="ResetVR",bShift=False,bCtrl=False,bAlt=False,bCmd=False,Key=Daydream_Left_Trackpad_Click) +ActionMappings=(ActionName="Jump",bShift=False,bCtrl=False,bAlt=False,bCmd=False,Key=Vive_Left_Trigger_Click) +ActionMappings=(ActionName="Jump",bShift=False,bCtrl=False,bAlt=False,bCmd=False,Key=Vive_Right_Trigger_Click) +ActionMappings=(ActionName="Jump",bShift=False,bCtrl=False,bAlt=False,bCmd=False,Key=MixedReality_Left_Trigger_Click) +ActionMappings=(ActionName="Jump",bShift=False,bCtrl=False,bAlt=False,bCmd=False,Key=MixedReality_Right_Trigger_Click) +ActionMappings=(ActionName="Jump",bShift=False,bCtrl=False,bAlt=False,bCmd=False,Key=OculusGo_Left_Trigger_Click) +ActionMappings=(ActionName="Jump",bShift=False,bCtrl=False,bAlt=False,bCmd=False,Key=OculusTouch_Left_Trigger_Click) +ActionMappings=(ActionName="Jump",bShift=False,bCtrl=False,bAlt=False,bCmd=False,Key=OculusTouch_Right_Trigger_Click) +ActionMappings=(ActionName="Jump",bShift=False,bCtrl=False,bAlt=False,bCmd=False,Key=ValveIndex_Left_Trigger_Click) +ActionMappings=(ActionName="Jump",bShift=False,bCtrl=False,bAlt=False,bCmd=False,Key=ValveIndex_Right_Trigger_Click) +ActionMappings=(ActionName="Jump",bShift=False,bCtrl=False,bAlt=False,bCmd=False,Key=MagicLeap_Left_Trigger) +ActionMappings=(ActionName="ResetVR",bShift=False,bCtrl=False,bAlt=False,bCmd=False,Key=Vive_Left_Grip_Click) +ActionMappings=(ActionName="ResetVR",bShift=False,bCtrl=False,bAlt=False,bCmd=False,Key=MixedReality_Left_Thumbstick_Click) +ActionMappings=(ActionName="ResetVR",bShift=False,bCtrl=False,bAlt=False,bCmd=False,Key=OculusGo_Left_Trackpad_Click) +ActionMappings=(ActionName="ResetVR",bShift=False,bCtrl=False,bAlt=False,bCmd=False,Key=OculusTouch_Left_Thumbstick_Click) +ActionMappings=(ActionName="ResetVR",bShift=False,bCtrl=False,bAlt=False,bCmd=False,Key=ValveIndex_Left_Thumbstick_Click) +ActionMappings=(ActionName="ResetVR",bShift=False,bCtrl=False,bAlt=False,bCmd=False,Key=MagicLeap_Left_Bumper) +AxisMappings=(AxisName="MoveForward",Scale=1.000000,Key=W) +AxisMappings=(AxisName="MoveForward",Scale=-1.000000,Key=S) +AxisMappings=(AxisName="MoveForward",Scale=1.000000,Key=Up) +AxisMappings=(AxisName="MoveForward",Scale=-1.000000,Key=Down) +AxisMappings=(AxisName="MoveForward",Scale=1.000000,Key=Gamepad_LeftY) +AxisMappings=(AxisName="MoveRight",Scale=-1.000000,Key=A) +AxisMappings=(AxisName="MoveRight",Scale=1.000000,Key=D) +AxisMappings=(AxisName="MoveRight",Scale=1.000000,Key=Gamepad_LeftX) +AxisMappings=(AxisName="TurnRate",Scale=1.000000,Key=Gamepad_RightX) +AxisMappings=(AxisName="TurnRate",Scale=-1.000000,Key=Left) +AxisMappings=(AxisName="TurnRate",Scale=1.000000,Key=Right) +AxisMappings=(AxisName="Turn",Scale=1.000000,Key=MouseX) +AxisMappings=(AxisName="LookUpRate",Scale=1.000000,Key=Gamepad_RightY) +AxisMappings=(AxisName="LookUp",Scale=-1.000000,Key=MouseY) +AxisMappings=(AxisName="TurnRate",Scale=-1.000000,Key=Vive_Right_Trackpad_X) +AxisMappings=(AxisName="MoveForward",Scale=1.000000,Key=Daydream_Left_Trackpad_Y) +AxisMappings=(AxisName="MoveForward",Scale=1.000000,Key=Vive_Left_Trackpad_Y) +AxisMappings=(AxisName="MoveRight",Scale=1.000000,Key=Daydream_Left_Trackpad_X) +AxisMappings=(AxisName="MoveRight",Scale=1.000000,Key=Vive_Left_Trackpad_X) +AxisMappings=(AxisName="MoveRight",Scale=1.000000,Key=MixedReality_Left_Thumbstick_X) +AxisMappings=(AxisName="MoveRight",Scale=1.000000,Key=OculusGo_Left_Trackpad_X) +AxisMappings=(AxisName="MoveForward",Scale=1.000000,Key=MixedReality_Left_Thumbstick_Y) +AxisMappings=(AxisName="MoveForward",Scale=1.000000,Key=OculusGo_Left_Trackpad_Y) +AxisMappings=(AxisName="TurnRate",Scale=-1.000000,Key=MixedReality_Right_Thumbstick_X) +AxisMappings=(AxisName="TurnRate",Scale=-1.000000,Key=OculusTouch_Right_Thumbstick_X) +AxisMappings=(AxisName="TurnRate",Scale=-1.000000,Key=ValveIndex_Right_Thumbstick_X) +AxisMappings=(AxisName="MoveForward",Scale=1.000000,Key=OculusTouch_Left_Thumbstick_Y) +AxisMappings=(AxisName="MoveForward",Scale=1.000000,Key=ValveIndex_Left_Thumbstick_Y) +AxisMappings=(AxisName="MoveForward",Scale=1.000000,Key=MagicLeap_Left_Trackpad_Y) +AxisMappings=(AxisName="MoveRight",Scale=1.000000,Key=OculusTouch_Left_Thumbstick_X) +AxisMappings=(AxisName="MoveRight",Scale=1.000000,Key=ValveIndex_Left_Thumbstick_X) +AxisMappings=(AxisName="MoveRight",Scale=1.000000,Key=MagicLeap_Left_Trackpad_X) DefaultPlayerInputClass=/Script/EnhancedInput.EnhancedPlayerInput DefaultInputComponentClass=/Script/EnhancedInput.EnhancedInputComponent DefaultTouchInterface=/Engine/MobileResources/HUD/DefaultVirtualJoysticks.DefaultVirtualJoysticks -ConsoleKeys=Tilde +ConsoleKeys=Tilde ``` `SampleProject/Config/HoloLens/HoloLensEngine.ini`: ```ini [/Script/HoloLensPlatformEditor.HoloLensTargetSettings] bBuildForEmulation=False bBuildForDevice=True bUseNameForLogo=True bBuildForRetailWindowsStore=False bAutoIncrementVersion=False bShouldCreateAppInstaller=False AppInstallerInstallationURL= HoursBetweenUpdateChecks=0 bEnablePIXProfiling=False TileBackgroundColor=(B=64,G=0,R=0,A=255) SplashScreenBackgroundColor=(B=64,G=0,R=0,A=255) +PerCultureResources=(CultureId="",Strings=(PackageDisplayName="",PublisherDisplayName="",PackageDescription="",ApplicationDisplayName="",ApplicationDescription=""),Images=()) TargetDeviceFamily=Windows.Holographic MinimumPlatformVersion= MaximumPlatformVersionTested=10.0.18362.0 MaxTrianglesPerCubicMeter=500.000000 SpatialMeshingVolumeSize=20.000000 CompilerVersion=Default Windows10SDKVersion=10.0.18362.0 +CapabilityList=internetClientServer +CapabilityList=privateNetworkClientServer +Uap2CapabilityList=spatialPerception bSetDefaultCapabilities=False SpatializationPlugin= ReverbPlugin= OcclusionPlugin= SoundCueCookQualityIndex=-1 ``` `SampleProject/Plugins/GameDebugMenu/Config/DefaultGameDebugMenu.ini`: ```ini [CoreRedirects] +ClassRedirects=(OldName="/Script/GameDebugMenu.GameDebugMenuDataAsset",NewName="/Script/GameDebugMenu.GameDebugMenuManagerAsset") ``` `SampleProject/Plugins/GameDebugMenu/GameDebugMenu.uplugin`: ```uplugin { "FileVersion": 3, "Version": 2, "VersionName": "1.38", "FriendlyName": "GameDebugMenu", "Description": "DebugMenu for UnrealEngine5", "Category": "Debug", "CreatedBy": "akihiko moroi", "CreatedByURL": "https://github.com/000-aki-000/GameDebugMenu", "DocsURL": "", "MarketplaceURL": "", "SupportURL": "", "CanContainContent": true, "IsBetaVersion": false, "Installed": false, "Modules": [ { "Name": "GameDebugMenu", "Type": "Runtime", "LoadingPhase": "Default" }, { "Name": "GameDebugMenuEditor", "Type": "Editor", "LoadingPhase": "PostDefault" } ], "Plugins": [ { "Name": "EnhancedInput", "Enabled": true } ], "IsExperimentalVersion": false } ``` `SampleProject/Plugins/GameDebugMenu/LICENSE`: ``` MIT License Copyright (c) 2020-2022 000-aki-000 Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ``` `SampleProject/Plugins/GameDebugMenu/README.md`: ```md ## License MIT ``` `SampleProject/Plugins/GameDebugMenu/Source/GameDebugMenu/GameDebugMenu.Build.cs`: ```cs /** * Copyright (c) 2020 akihiko moroi * * This software is released under the MIT License. * (See accompanying file LICENSE.txt or copy at http://opensource.org/licenses/MIT) */ using UnrealBuildTool; using System.IO; public class GameDebugMenu : ModuleRules { public GameDebugMenu(ReadOnlyTargetRules Target) : base(Target) { PublicDependencyModuleNames.AddRange( [ "Core", "UMG", "Engine", "InputCore", "Slate", "SlateCore", "HTTP", "Json", "JsonUtilities", "ImageDownload", "ImageWrapper", "EngineSettings", "EnhancedInput" ] ); PrivateDependencyModuleNames.AddRange( [ "CoreUObject", "Engine", "Slate", "SlateCore", "DeveloperSettings", "EnhancedInput" ] ); } } ``` `SampleProject/Plugins/GameDebugMenu/Source/GameDebugMenu/Private/Component/GDMListenerComponent.cpp`: ```cpp /** * Copyright (c) 2020 akihiko moroi * * This software is released under the MIT License. * (See accompanying file LICENSE.txt or copy at http://opensource.org/licenses/MIT) */ #include "Component/GDMListenerComponent.h" #include static TMap>> GlobalListenerComponents; UGDMListenerComponent::UGDMListenerComponent(const FObjectInitializer& ObjectInitializer) : Super(ObjectInitializer) { PrimaryComponentTick.bCanEverTick = false; PrimaryComponentTick.bStartWithTickEnabled = false; bNeverNeedsRenderUpdate = true; bWantsInitializeComponent = true; } void UGDMListenerComponent::InitializeComponent() { Super::InitializeComponent(); PushListenerComponent(GetWorld(),this); } void UGDMListenerComponent::UninitializeComponent() { PopListenerComponent(GetWorld(),this); Super::UninitializeComponent(); } void UGDMListenerComponent::AllUnbindDispatchers() { OnShowDispatcher.Clear(); OnHideDispatcher.Clear(); OnExecuteConsoleCommandDispatcher.Clear(); OnExecuteProcessEventDispatcher.Clear(); OnChangePropertyBoolDispatcher.Clear(); OnChangePropertyIntDispatcher.Clear(); OnChangePropertyFloatDispatcher.Clear(); OnChangePropertyByteDispatcher.Clear(); OnChangePropertyStringDispatcher.Clear(); OnChangePropertyVectorDispatcher.Clear(); OnChangePropertyVector2DDispatcher.Clear(); OnChangePropertyRotatorDispatcher.Clear(); OnChangeDebugMenuLanguageDispatcher.Clear(); OnStartScreenshotRequestDispatcher.Clear(); OnScreenshotRequestProcessedDispatcher.Clear(); OnLoadedDebugMenuDispatcher.Clear(); OnSavedDebugMenuDispatcher.Clear(); OnDeletedDebugMenuDispatcher.Clear(); } int32 UGDMListenerComponent::PushListenerComponent(UWorld* TargetWorld, UGDMListenerComponent* Listener) { TArray>& ListenerComponents = GlobalListenerComponents.FindOrAdd(TargetWorld); return ListenerComponents.AddUnique(Listener); } int32 UGDMListenerComponent::PopListenerComponent(UWorld* TargetWorld, UGDMListenerComponent* Listener) { TArray>& ListenerComponents = GlobalListenerComponents.FindOrAdd(TargetWorld); return ListenerComponents.Remove(Listener); } void UGDMListenerComponent::GetAllListenerComponents(UWorld* TargetWorld, TArray& OutListenerComponents) { if( IsValid(TargetWorld) ) { TArray>& ListenerComponents = GlobalListenerComponents.FindOrAdd(TargetWorld); OutListenerComponents.Reserve(ListenerComponents.Num()); for( int32 Index = ListenerComponents.Num() - 1; Index >= 0; --Index ) { if( ListenerComponents[Index].IsValid() ) { OutListenerComponents.Add(ListenerComponents[Index].Get()); } else { ListenerComponents.RemoveAt(Index); } } if( ListenerComponents.Num() <= 0 ) { GlobalListenerComponents.Remove(TargetWorld); } } } ``` `SampleProject/Plugins/GameDebugMenu/Source/GameDebugMenu/Private/Component/GDMLocalizeStringComponent.cpp`: ```cpp /** * Copyright (c) 2020 akihiko moroi * * This software is released under the MIT License. * (See accompanying file LICENSE.txt or copy at http://opensource.org/licenses/MIT) */ #include "Component/GDMLocalizeStringComponent.h" #include "GameDebugMenuSettings.h" #include "Component/GDMPropertyJsonSystemComponent.h" #include "Internationalization/StringTableCore.h" UGDMLocalizeStringComponent::UGDMLocalizeStringComponent() : CachedDebugMenuStrings() , bCurrentDebugMenuDirectStringKey(false) , CurrentLanguage(NAME_None) { PrimaryComponentTick.bCanEverTick = false; PrimaryComponentTick.bStartWithTickEnabled = false; } void UGDMLocalizeStringComponent::SetJsonSystemComponentValue(UGDMPropertyJsonSystemComponent* PropertyJsonSystemComponent) { FString StringKey = TEXT("DebugMenuDirectStringKey"); { if (!PropertyJsonSystemComponent->HasCustomString(StringKey)) { PropertyJsonSystemComponent->SetCustomString(StringKey, TEXT("False")); } bCurrentDebugMenuDirectStringKey = PropertyJsonSystemComponent->GetCustomString(StringKey, TEXT("False")).ToBool(); } StringKey = TEXT("DebugMenuLanguage"); { if (!PropertyJsonSystemComponent->HasCustomString(StringKey)) { PropertyJsonSystemComponent->SetCustomString(StringKey, GetDefault()->DefaultGameDebugMenuLanguage.ToString()); } CurrentLanguage = *PropertyJsonSystemComponent->GetCustomString(StringKey, GetDefault()->DefaultGameDebugMenuLanguage.ToString()); } } void UGDMLocalizeStringComponent::SetToJsonSystemComponent(UGDMPropertyJsonSystemComponent* PropertyJsonSystemComponent, const FString& Language) { CurrentLanguage = *Language; PropertyJsonSystemComponent->SetCustomString(TEXT("DebugMenuDirectStringKey"), bCurrentDebugMenuDirectStringKey ? TEXT("True") : TEXT("False")); PropertyJsonSystemComponent->SetCustomString(TEXT("DebugMenuLanguage"), Language); } void UGDMLocalizeStringComponent::SyncLoadDebugMenuStringTables() { CachedDebugMenuStrings.Reset(); if( const FGDMStringTableList* StringTableList = GetDefault()->TryGetStringTableList(CurrentLanguage) ) { UE_LOG(LogGDM, Verbose, TEXT("Call SyncLoadDebugMenuStringTables %s"), *CurrentLanguage.ToString()); for( auto& StrTablePtr : StringTableList->StringTables ) { if( !StrTablePtr.ToSoftObjectPath().IsValid() || StrTablePtr.ToSoftObjectPath().IsNull() ) { UE_LOG(LogGDM, Warning, TEXT("SyncLoadDebugMenuStringTables: failed StringTable : LanguageKey->%s"), *CurrentLanguage.ToString()); continue; } if(const UStringTable* StringTable = StrTablePtr.LoadSynchronous() ) { StringTable->GetStringTable()->EnumerateSourceStrings([&](const FString& InKey, const FString& InSourceString) -> bool { if( !CachedDebugMenuStrings.Contains(InKey) ) { CachedDebugMenuStrings.Add(InKey, InSourceString); UE_LOG(LogGDM, VeryVerbose, TEXT("Load string | Key %s SourceString %s"), *InKey, *InSourceString); } else { UE_LOG(LogGDM, Error, TEXT("%s -> StringKey that is already in use!!"), *InKey); } return true; /* すべて取得する */ }); } } } else { UE_LOG(LogGDM, Error, TEXT("Failed Load DebugMenuStringTables %s"), *CurrentLanguage.ToString()); } } ``` `SampleProject/Plugins/GameDebugMenu/Source/GameDebugMenu/Private/Component/GDMPlayerControllerProxyComponent.cpp`: ```cpp /** * Copyright (c) 2020 akihiko moroi * * This software is released under the MIT License. * (See accompanying file LICENSE.txt or copy at http://opensource.org/licenses/MIT) */ #include "Component/GDMPlayerControllerProxyComponent.h" #include #include "GameFramework/PlayerController.h" #include "GameFramework/Character.h" #include "GameDebugMenuFunctions.h" #include "Net/UnrealNetwork.h" UGDMPlayerControllerProxyComponent::UGDMPlayerControllerProxyComponent() { PrimaryComponentTick.bCanEverTick = false; PrimaryComponentTick.bStartWithTickEnabled = false; SetIsReplicatedByDefault(true); } void UGDMPlayerControllerProxyComponent::GetLifetimeReplicatedProps(TArray& OutLifetimeProps ) const { Super::GetLifetimeReplicatedProps( OutLifetimeProps ); DOREPLIFETIME( UGDMPlayerControllerProxyComponent, DebugMenuManager ); } void UGDMPlayerControllerProxyComponent::BeginPlay() { Super::BeginPlay(); UE_LOG(LogGDM, Log, TEXT("Call BeginPlay Spawn GDMPlayerControllerProxyComponent")); } void UGDMPlayerControllerProxyComponent::EndPlay(const EEndPlayReason::Type EndPlayReason) { Super::EndPlay(EndPlayReason); UE_LOG(LogGDM, Log, TEXT("Call EndPlay Destroyed GDMPlayerControllerProxyComponent")); } APlayerController* UGDMPlayerControllerProxyComponent::GetOwnerPlayerController() const { return Cast(GetOwner()); } APawn* UGDMPlayerControllerProxyComponent::GetOwnerPlayerPawn() const { const APlayerController* PlayerController = GetOwnerPlayerController(); if (!IsValid(PlayerController)) { return nullptr; } return PlayerController->GetPawn(); } ACharacter* UGDMPlayerControllerProxyComponent::GetOwnerPlayerCharacter() const { const APlayerController* PlayerController = GetOwnerPlayerController(); if (IsValid(PlayerController)) { return nullptr; } return PlayerController->GetCharacter(); } AGameDebugMenuManager* UGDMPlayerControllerProxyComponent::GetDebugMenuManager() const { return DebugMenuManager; } void UGDMPlayerControllerProxyComponent::ROS_ExecuteConsoleCommand_Implementation(const FString& Command, bool bAllClient) { if (bAllClient) { AllExecuteConsoleCommand_Server(Command); } else { ROC_ExecuteConsoleCommand(Command); } } bool UGDMPlayerControllerProxyComponent::ROS_ExecuteConsoleCommand_Validate(const FString& Command, bool bAllClient) { return (!Command.IsEmpty()); } void UGDMPlayerControllerProxyComponent::ROC_ExecuteConsoleCommand_Implementation(const FString& Command) { GetDebugMenuManager()->ExecuteConsoleCommand(Command, GetOwnerPlayerController()); } bool UGDMPlayerControllerProxyComponent::ROC_ExecuteConsoleCommand_Validate(const FString& Command) { return (!Command.IsEmpty()); } void UGDMPlayerControllerProxyComponent::ExecuteConsoleCommand(const FString& Command, EGDMConsoleCommandNetType CommandNetType) { switch (CommandNetType) { case EGDMConsoleCommandNetType::LocalOnly: { /* 通信せず実行者の環境で実行する */ GetDebugMenuManager()->ExecuteConsoleCommand(Command, GetOwnerPlayerController()); break; } case EGDMConsoleCommandNetType::ServerAll: { /* Serverで全プレイヤーに実行 */ if (GetOwner()->HasAuthority()) { AllExecuteConsoleCommand_Server(Command); } else { ROS_ExecuteConsoleCommand(Command,true); } break; } default: { break; } } } void UGDMPlayerControllerProxyComponent::AllExecuteConsoleCommand_Server(const FString& Command) { for (FConstPlayerControllerIterator Iterator = GetWorld()->GetPlayerControllerIterator(); Iterator; ++Iterator) { APlayerController* PC = Iterator->Get(); if (!IsValid(PC)) { continue; } GetDebugMenuManager()->ExecuteConsoleCommand(Command, PC); } } ``` `SampleProject/Plugins/GameDebugMenu/Source/GameDebugMenu/Private/Component/GDMPropertyJsonSystemComponent.cpp`: ```cpp /** * Copyright (c) 2020 akihiko moroi * * This software is released under the MIT License. * (See accompanying file LICENSE.txt or copy at http://opensource.org/licenses/MIT) */ #include "Component/GDMPropertyJsonSystemComponent.h" #include "GameDebugMenuFunctions.h" #include "Component/GDMListenerComponent.h" const FString UGDMPropertyJsonSystemComponent::JsonField_RootProperty(TEXT("Properties")); const FString UGDMPropertyJsonSystemComponent::JsonField_RootFunction(TEXT("Functions")); const FString UGDMPropertyJsonSystemComponent::JsonField_RootCustom(TEXT("Custom")); const FString UGDMPropertyJsonSystemComponent::JsonField_RootFavorite(TEXT("Favorites")); const FString UGDMPropertyJsonSystemComponent::JsonField_FavoriteDefinitionName(TEXT("DefinitionName")); const FString UGDMPropertyJsonSystemComponent::JsonField_FavoriteSaveKey(TEXT("SaveKey")); UGDMPropertyJsonSystemComponent::UGDMPropertyJsonSystemComponent(const FObjectInitializer& ObjectInitializer) : Super(ObjectInitializer) { RootJsonObject = MakeShared(); } void UGDMPropertyJsonSystemComponent::BeginPlay() { Super::BeginPlay(); /* OwnerはAGameDebugMenuManagerであること前提 */ UGDMListenerComponent* ListenerComp = GetOwner()->GetComponentByClass(); ListenerComp->OnChangePropertyBoolDispatcher.AddUniqueDynamic(this, &UGDMPropertyJsonSystemComponent::OnChangePropertyBool); ListenerComp->OnChangePropertyIntDispatcher.AddUniqueDynamic(this, &UGDMPropertyJsonSystemComponent::OnChangePropertyInt); ListenerComp->OnChangePropertyFloatDispatcher.AddUniqueDynamic(this, &UGDMPropertyJsonSystemComponent::OnChangePropertyFloat); ListenerComp->OnChangePropertyByteDispatcher.AddUniqueDynamic(this, &UGDMPropertyJsonSystemComponent::OnChangePropertyByte); ListenerComp->OnChangePropertyStringDispatcher.AddUniqueDynamic(this, &UGDMPropertyJsonSystemComponent::OnChangePropertyString); ListenerComp->OnChangePropertyVectorDispatcher.AddUniqueDynamic(this, &UGDMPropertyJsonSystemComponent::OnChangePropertyVector); ListenerComp->OnChangePropertyVector2DDispatcher.AddUniqueDynamic(this, &UGDMPropertyJsonSystemComponent::OnChangePropertyVector2D); ListenerComp->OnChangePropertyRotatorDispatcher.AddUniqueDynamic(this, &UGDMPropertyJsonSystemComponent::OnChangePropertyRotator); } void UGDMPropertyJsonSystemComponent::AddPropertyToJson(const FString& ObjectKey, UObject* TargetObject, const FString& PropertyName) const { if (ObjectKey.IsEmpty()) { UE_LOG(LogGDM, Warning, TEXT("AddPropertyToJson: ObjectKey is empty.")); return; } if (!IsValid(TargetObject)) { UE_LOG(LogGDM, Warning, TEXT("AddPropertyToJson: TargetObject is null.")); return; } if (PropertyName.IsEmpty()) { UE_LOG(LogGDM, Warning, TEXT("AddPropertyToJson: PropertyName is empty.")); return; } const FProperty* Property = TargetObject->GetClass()->FindPropertyByName(*PropertyName); if (!Property) { UE_LOG(LogGDM, Warning, TEXT("AddPropertyToJson: Property '%s' not found in object '%s'."), *PropertyName, *TargetObject->GetName()); return; } FString PropertyValue; const void* PropertyValuePtr = Property->ContainerPtrToValuePtr(TargetObject); if (!Property->ExportText_Direct(PropertyValue, PropertyValuePtr, nullptr, TargetObject, PPF_None)) { Property->ExportTextItem_Direct(PropertyValue, PropertyValuePtr, nullptr, TargetObject, PPF_None); } const TSharedPtr* RootPropertyJson = nullptr; if (!RootJsonObject->TryGetObjectField(JsonField_RootProperty, RootPropertyJson)) { /* フィールドが存在しない場合、新しいオブジェクトを作成 */ TSharedPtr NewJsonObject = MakeShareable(new FJsonObject()); RootJsonObject->SetObjectField(JsonField_RootProperty, NewJsonObject); RootPropertyJson = &NewJsonObject; } const TSharedPtr* ObjectJson = nullptr; if (!(*RootPropertyJson)->TryGetObjectField(ObjectKey, ObjectJson)) { /* フィールドが存在しない場合、新しいオブジェクトを作成 */ TSharedPtr NewJsonObject = MakeShareable(new FJsonObject()); (*RootPropertyJson)->SetObjectField(ObjectKey, NewJsonObject); ObjectJson = &NewJsonObject; } (*ObjectJson)->SetStringField(PropertyName, PropertyValue); UE_LOG(LogGDM, Verbose, TEXT("AddPropertyToJson: Added property '%s' with value '%s' to '%s'."), *PropertyName, *PropertyValue, *ObjectKey); } void UGDMPropertyJsonSystemComponent::RemovePropertyFromJson(const FString& ObjectKey, const FString& PropertyName) const { if (ObjectKey.IsEmpty()) { UE_LOG(LogGDM, Warning, TEXT("RemovePropertyFromJson: ObjectKey is empty.")); return; } if (PropertyName.IsEmpty()) { UE_LOG(LogGDM, Warning, TEXT("RemovePropertyFromJson: PropertyName is empty.")); return; } const TSharedPtr* RootPropertyJson = nullptr; if (!RootJsonObject->TryGetObjectField(JsonField_RootProperty, RootPropertyJson)) { UE_LOG(LogGDM, Verbose, TEXT("RemovePropertyFromJson: Property '%s' not found"), *JsonField_RootProperty); return; } const TSharedPtr* ObjectJson = nullptr; if ((*RootPropertyJson)->TryGetObjectField(ObjectKey, ObjectJson)) { if ((*ObjectJson)->HasField(PropertyName)) { (*ObjectJson)->RemoveField(PropertyName); UE_LOG(LogGDM, Verbose, TEXT("RemovePropertyFromJson: Removed property '%s' from '%s'."), *PropertyName, *ObjectKey); } else { UE_LOG(LogGDM, Verbose, TEXT("RemovePropertyFromJson: Property '%s' not found in '%s'."), *PropertyName, *ObjectKey); } } else { UE_LOG(LogGDM, Verbose, TEXT("RemovePropertyFromJson: ObjectKey '%s' not found."), *ObjectKey); } } bool UGDMPropertyJsonSystemComponent::ApplyJsonToObjectProperty(const FString& ObjectKey, UObject* TargetObject, const FString& PropertyName) const { if (ObjectKey.IsEmpty()) { UE_LOG(LogGDM, Warning, TEXT("ApplyJsonToObjectProperty ObjectKey is empty.")); return false; } if (!IsValid(TargetObject)) { UE_LOG(LogGDM, Warning, TEXT("ApplyJsonToObjectProperty TargetObject is nullptr.")); return false; } if (PropertyName.IsEmpty()) { UE_LOG(LogGDM, Warning, TEXT("ApplyJsonToObjectProperty PropertyName is empty.")); return false; } const TSharedPtr* RootPropertyJson = nullptr; if (!RootJsonObject->TryGetObjectField(JsonField_RootProperty, RootPropertyJson)) { UE_LOG(LogGDM, Verbose, TEXT("ApplyJsonToObjectProperty Property '%s' not found"), *JsonField_RootProperty); return false; } const TSharedPtr* ObjectJson = nullptr; if (!(*RootPropertyJson)->TryGetObjectField(ObjectKey, ObjectJson)) { UE_LOG(LogGDM, Verbose, TEXT("ApplyJsonToObjectProperty ObjectKey '%s' not found in JSON."), *ObjectKey); return false; } FString PropertyValue; if (!(*ObjectJson)->TryGetStringField(PropertyName, PropertyValue)) { UE_LOG(LogGDM, Verbose, TEXT("ApplyJsonToObjectProperty Property '%s' not found in JSON for object '%s'."), *PropertyName, *ObjectKey); return false; } const FProperty* Property = TargetObject->GetClass()->FindPropertyByName(*PropertyName); if (Property == nullptr) { UE_LOG(LogGDM, Warning, TEXT("ApplyJsonToObjectProperty Property '%s' not found in target object '%s'."), *PropertyName, *TargetObject->GetName()); return false; } void* PropertyValuePtr = Property->ContainerPtrToValuePtr(TargetObject); if (!Property->ImportText_Direct(*PropertyValue, PropertyValuePtr, nullptr, PPF_None)) { UE_LOG(LogGDM, Verbose, TEXT("ApplyJsonToObjectProperty Failed to set property '%s' with value '%s' for object '%s'."), *PropertyName, *PropertyValue, *ObjectKey); return false; } UE_LOG(LogGDM, Verbose, TEXT("ApplyJsonToObjectProperty Successfully set property '%s' with value '%s' for object '%s'."), *PropertyName, *PropertyValue, *ObjectKey); return true; } void UGDMPropertyJsonSystemComponent::AddFunctionToJson(const FString& ObjectKey, UObject* TargetObject, const FString& FunctionName) const { if (ObjectKey.IsEmpty()) { UE_LOG(LogGDM, Warning, TEXT("AddFunctionToJson: ObjectKey is empty.")); return; } if (!IsValid(TargetObject)) { UE_LOG(LogGDM, Warning, TEXT("AddFunctionToJson: TargetObject is null.")); return; } if (FunctionName.IsEmpty()) { UE_LOG(LogGDM, Warning, TEXT("AddFunctionToJson: FunctionName is empty.")); return; } const UFunction* Function = TargetObject->GetClass()->FindFunctionByName(*FunctionName); if (!IsValid(Function)) { UE_LOG(LogGDM, Warning, TEXT("AddFunctionToJson: Function '%s' not found in object '%s'."), *FunctionName, *TargetObject->GetName()); return; } const TSharedPtr* RootFunctionJson = nullptr; if (!RootJsonObject->TryGetObjectField(JsonField_RootFunction, RootFunctionJson)) { /* フィールドが存在しない場合、新しいオブジェクトを作成 */ TSharedPtr NewJsonObject = MakeShareable(new FJsonObject()); RootJsonObject->SetObjectField(JsonField_RootFunction, NewJsonObject); RootFunctionJson = &NewJsonObject; } const TSharedPtr* ObjectJson = nullptr; if (!(*RootFunctionJson)->TryGetObjectField(ObjectKey, ObjectJson)) { /* フィールドが存在しない場合、新しいオブジェクトを作成 */ TSharedPtr NewJsonObject = MakeShareable(new FJsonObject()); (*RootFunctionJson)->SetObjectField(ObjectKey, NewJsonObject); ObjectJson = &NewJsonObject; } (*ObjectJson)->SetBoolField(FunctionName, true); UE_LOG(LogGDM, Verbose, TEXT("AddFunctionToJson: Added function '%s' with to '%s'."), *FunctionName, *ObjectKey); } void UGDMPropertyJsonSystemComponent::RemoveFunctionFromJson(const FString& ObjectKey, const FString& FunctionName) const { if (ObjectKey.IsEmpty()) { UE_LOG(LogGDM, Warning, TEXT("RemoveFunctionFromJson: ObjectKey is empty.")); return; } if (FunctionName.IsEmpty()) { UE_LOG(LogGDM, Warning, TEXT("RemoveFunctionFromJson: FunctionName is empty.")); return; } const TSharedPtr* RootFunctionJson = nullptr; if (!RootJsonObject->TryGetObjectField(JsonField_RootFunction, RootFunctionJson)) { UE_LOG(LogGDM, Verbose, TEXT("RemoveFunctionFromJson: Function '%s' not found"), *JsonField_RootFunction); return; } const TSharedPtr* ObjectJson = nullptr; if ((*RootFunctionJson)->TryGetObjectField(ObjectKey, ObjectJson)) { if ((*ObjectJson)->HasField(FunctionName)) { (*ObjectJson)->RemoveField(FunctionName); UE_LOG(LogGDM, Verbose, TEXT("RemoveFunctionFromJson: Removed function '%s' from '%s'."), *FunctionName, *ObjectKey); } else { UE_LOG(LogGDM, Verbose, TEXT("RemoveFunctionFromJson: Function '%s' not found in '%s'."), *FunctionName, *ObjectKey); } } else { UE_LOG(LogGDM, Verbose, TEXT("RemoveFunctionFromJson: ObjectKey '%s' not found."), *ObjectKey); } } bool UGDMPropertyJsonSystemComponent::HaveFunctionInJson(const FString& ObjectKey, UObject* TargetObject, const FString& FunctionName) const { if (ObjectKey.IsEmpty()) { UE_LOG(LogGDM, Warning, TEXT("HaveFunctionInJson ObjectKey is empty.")); return false; } if (!IsValid(TargetObject)) { UE_LOG(LogGDM, Warning, TEXT("HaveFunctionInJson TargetObject is nullptr.")); return false; } if (FunctionName.IsEmpty()) { UE_LOG(LogGDM, Warning, TEXT("HaveFunctionInJson FunctionName is empty.")); return false; } const UFunction* Function = TargetObject->GetClass()->FindFunctionByName(*FunctionName); if (!IsValid(Function)) { UE_LOG(LogGDM, Warning, TEXT("HaveFunctionInJson Function '%s' not found in target object '%s'."), *FunctionName, *TargetObject->GetName()); return false; } const TSharedPtr* RootFunctionJson = nullptr; if (!RootJsonObject->TryGetObjectField(JsonField_RootFunction, RootFunctionJson)) { UE_LOG(LogGDM, Verbose, TEXT("HaveFunctionInJson Function '%s' not found"), *JsonField_RootFunction); return false; } const TSharedPtr* ObjectJson = nullptr; if (!(*RootFunctionJson)->TryGetObjectField(ObjectKey, ObjectJson)) { UE_LOG(LogGDM, Verbose, TEXT("HaveFunctionInJson ObjectKey '%s' not found in JSON."), *ObjectKey); return false; } return (*ObjectJson)->HasField(FunctionName); } void UGDMPropertyJsonSystemComponent::AddFavoriteEntry(const FString& DefinitionName, const FString& FavoriteSaveKey) { if (HasFavoriteEntry(DefinitionName, FavoriteSaveKey)) { UE_LOG(LogGDM, Log, TEXT("AddFavoriteEntry: already exists (%s, %s)"), *DefinitionName, *FavoriteSaveKey); return; } TSharedPtr Entry = MakeShared(); Entry->SetStringField(JsonField_FavoriteDefinitionName, DefinitionName); Entry->SetStringField(JsonField_FavoriteSaveKey, FavoriteSaveKey); TArray> Array = RootJsonObject->HasTypedField(JsonField_RootFavorite) ? RootJsonObject->GetArrayField(JsonField_RootFavorite) : TArray>(); Array.Add(MakeShared(Entry)); RootJsonObject->SetArrayField(JsonField_RootFavorite, Array); UE_LOG(LogGDM, Verbose, TEXT("AddFavoriteEntry: DefinitionName %s, FavoriteSaveKey %s "), *DefinitionName, *FavoriteSaveKey); } bool UGDMPropertyJsonSystemComponent::RemoveFavoriteEntry(const FString& DefinitionName, const FString& FavoriteSaveKey) { if (!RootJsonObject->HasTypedField(JsonField_RootFavorite)) { return false; } TArray> Array = RootJsonObject->GetArrayField(JsonField_RootFavorite); const int32 OriginalCount = Array.Num(); Array.RemoveAll([&](const TSharedPtr& Value) { const TSharedPtr* ObjPtr = nullptr; if (Value->TryGetObject(ObjPtr)) { return (*ObjPtr)->GetStringField(JsonField_FavoriteDefinitionName) == DefinitionName && (*ObjPtr)->GetStringField(JsonField_FavoriteSaveKey) == FavoriteSaveKey; } return false; }); if (Array.Num() != OriginalCount) { /* 減ったら再セット */ RootJsonObject->SetArrayField(JsonField_RootFavorite, Array); UE_LOG(LogGDM, Verbose, TEXT("RemoveFavoriteEntry: DefinitionName %s, FavoriteSaveKey %s '%d'->'%d'"), *DefinitionName, *FavoriteSaveKey, OriginalCount, Array.Num()); return true; } return false; } bool UGDMPropertyJsonSystemComponent::HasFavoriteEntry(const FString& DefinitionName, const FString& FavoriteSaveKey) const { if (!RootJsonObject->HasTypedField(JsonField_RootFavorite)) { return false; } const TArray> Array = RootJsonObject->GetArrayField(JsonField_RootFavorite); for (const TSharedPtr& Value : Array) { const TSharedPtr* ObjPtr = nullptr; if (Value->TryGetObject(ObjPtr)) { if ((*ObjPtr)->GetStringField(JsonField_FavoriteDefinitionName) == DefinitionName && (*ObjPtr)->GetStringField(JsonField_FavoriteSaveKey) == FavoriteSaveKey) { return true; } } } return false; } TArray UGDMPropertyJsonSystemComponent::GetAllFavoriteEntries() const { TArray OutEntries; if (!RootJsonObject->HasTypedField(JsonField_RootFavorite)) { return OutEntries; } const TArray> JsonArray = RootJsonObject->GetArrayField(JsonField_RootFavorite); for (const TSharedPtr& Value : JsonArray) { const TSharedPtr* ObjPtr = nullptr; if (Value->TryGetObject(ObjPtr)) { FGDMFavoriteEntry Entry; Entry.DefinitionName = (*ObjPtr)->GetStringField(JsonField_FavoriteDefinitionName); Entry.SaveKey = (*ObjPtr)->GetStringField(JsonField_FavoriteSaveKey); OutEntries.Add(Entry); } } return OutEntries; } void UGDMPropertyJsonSystemComponent::SetCustomStringArray(const FString& Key, const TArray& StringArray) { if (Key.IsEmpty()) { UE_LOG(LogGDM, Warning, TEXT("SetCustomStringArray: Key is empty.")); return; } TSharedPtr RootCustomJson; if (RootJsonObject->HasTypedField(JsonField_RootCustom)) { RootCustomJson = RootJsonObject->GetObjectField(JsonField_RootCustom); } else { RootCustomJson = MakeShared(); RootJsonObject->SetObjectField(JsonField_RootCustom, RootCustomJson); } TArray> JsonArray; for (const FString& Value : StringArray) { JsonArray.Add(MakeShared(Value)); } RootCustomJson->SetArrayField(Key, JsonArray); UE_LOG(LogGDM, Verbose, TEXT("SetCustomStringArray: Added array under key '%s'"), *Key); } void UGDMPropertyJsonSystemComponent::SetCustomString(const FString& Key, const FString& StringValue) { if (Key.IsEmpty()) { UE_LOG(LogGDM, Warning, TEXT("SetCustomString: Key is empty.")); return; } TSharedPtr RootCustomJson; if (RootJsonObject->HasTypedField(JsonField_RootCustom)) { RootCustomJson = RootJsonObject->GetObjectField(JsonField_RootCustom); } else { RootCustomJson = MakeShared(); RootJsonObject->SetObjectField(JsonField_RootCustom, RootCustomJson); } RootCustomJson->SetStringField(Key, StringValue); UE_LOG(LogGDM, Verbose, TEXT("SetCustomString: Set '%s' to key '%s'."), *StringValue, *Key); } TArray UGDMPropertyJsonSystemComponent::GetCustomStringArray(const FString& Key) const { TArray Result; if (!HasCustomString(Key)) { return Result; } const TSharedPtr RootCustomJson = RootJsonObject->GetObjectField(JsonField_RootCustom); const TArray>* JsonArray = nullptr; if (RootCustomJson->TryGetArrayField(Key, JsonArray)) { for (const TSharedPtr& Value : *JsonArray) { if (Value->Type == EJson::String) { Result.Add(Value->AsString()); UE_LOG(LogGDM, Verbose, TEXT("GetCustomStringArray: Add string value '%s' '%s'."), *Key, *Value->AsString()); } else { UE_LOG(LogGDM, Warning, TEXT("GetCustomStringArray: Non-string value found in array for key '%s'."), *Key); } } } return Result; } FString UGDMPropertyJsonSystemComponent::GetCustomString(const FString& Key, const FString& DefaultValue) const { if (HasCustomString(Key)) { return RootJsonObject->GetObjectField(JsonField_RootCustom)->GetStringField(Key); } return DefaultValue; } bool UGDMPropertyJsonSystemComponent::HasCustomString(const FString& Key) const { if (Key.IsEmpty()) { UE_LOG(LogGDM, Warning, TEXT("HasCustomString: Key is empty.")); return false; } if (!RootJsonObject->HasTypedField(JsonField_RootCustom)) { return false; } return RootJsonObject->GetObjectField(JsonField_RootCustom)->HasField(Key); } FString UGDMPropertyJsonSystemComponent::GetJsonAsString() const { FString JsonString; const TSharedRef> Writer = TJsonWriterFactory<>::Create(&JsonString); if (FJsonSerializer::Serialize(RootJsonObject.ToSharedRef(), Writer)) { UE_LOG(LogGDM, Verbose, TEXT("GetJsonAsString: %s"), *JsonString); return JsonString; } return TEXT(""); } bool UGDMPropertyJsonSystemComponent::BuildJsonFromString(const FString& JsonString) { if (JsonString.IsEmpty()) { UE_LOG(LogGDM, Warning, TEXT("BuildJsonFromString: Input JSON string is empty.")); return false; } TSharedPtr ParsedJsonObject; TSharedRef> Reader = TJsonReaderFactory<>::Create(JsonString); if (!FJsonSerializer::Deserialize(Reader, ParsedJsonObject) || !ParsedJsonObject.IsValid()) { UE_LOG(LogGDM, Error, TEXT("BuildJsonFromString: Failed to parse JSON string.")); return false; } RootJsonObject = ParsedJsonObject; UE_LOG(LogGDM, Verbose, TEXT("BuildJsonFromString: Successfully updated RootJsonObject.")); return true; } void UGDMPropertyJsonSystemComponent::OnChangePropertyBool(const FName& PropertyName, UObject* PropertyOwnerObject, bool New, bool Old, const FString& PropertySaveKey) { if (!PropertySaveKey.IsEmpty()) { AddPropertyToJson(PropertySaveKey, PropertyOwnerObject, PropertyName.ToString()); } } void UGDMPropertyJsonSystemComponent::OnChangePropertyInt(const FName& PropertyName, UObject* PropertyOwnerObject, int32 New, int32 Old, const FString& PropertySaveKey) { if (!PropertySaveKey.IsEmpty()) { AddPropertyToJson(PropertySaveKey, PropertyOwnerObject, PropertyName.ToString()); } } void UGDMPropertyJsonSystemComponent::OnChangePropertyFloat(const FName& PropertyName, UObject* PropertyOwnerObject, float New, float Old, const FString& PropertySaveKey) { if (!PropertySaveKey.IsEmpty()) { AddPropertyToJson(PropertySaveKey, PropertyOwnerObject, PropertyName.ToString()); } } void UGDMPropertyJsonSystemComponent::OnChangePropertyByte(const FName& PropertyName, UObject* PropertyOwnerObject, uint8 New, uint8 Old, const FString& PropertySaveKey) { if (!PropertySaveKey.IsEmpty()) { AddPropertyToJson(PropertySaveKey, PropertyOwnerObject, PropertyName.ToString()); } } void UGDMPropertyJsonSystemComponent::OnChangePropertyString(const FName& PropertyName, UObject* PropertyOwnerObject, FString New, FString Old, const FString& PropertySaveKey) { if (!PropertySaveKey.IsEmpty()) { AddPropertyToJson(PropertySaveKey, PropertyOwnerObject, PropertyName.ToString()); } } void UGDMPropertyJsonSystemComponent::OnChangePropertyVector(const FName& PropertyName, UObject* PropertyOwnerObject, FVector New, FVector Old, const FString& PropertySaveKey) { if (!PropertySaveKey.IsEmpty()) { AddPropertyToJson(PropertySaveKey, PropertyOwnerObject, PropertyName.ToString()); } } void UGDMPropertyJsonSystemComponent::OnChangePropertyVector2D(const FName& PropertyName, UObject* PropertyOwnerObject, FVector2D New, FVector2D Old, const FString& PropertySaveKey) { if (!PropertySaveKey.IsEmpty()) { AddPropertyToJson(PropertySaveKey, PropertyOwnerObject, PropertyName.ToString()); } } void UGDMPropertyJsonSystemComponent::OnChangePropertyRotator(const FName& PropertyName, UObject* PropertyOwnerObject, FRotator New, FRotator Old, const FString& PropertySaveKey) { if (!PropertySaveKey.IsEmpty()) { AddPropertyToJson(PropertySaveKey, PropertyOwnerObject, PropertyName.ToString()); } } ``` `SampleProject/Plugins/GameDebugMenu/Source/GameDebugMenu/Private/Component/GDMSaveSystemComponent.cpp`: ```cpp /** * Copyright (c) 2020 akihiko moroi * * This software is released under the MIT License. * (See accompanying file LICENSE.txt or copy at http://opensource.org/licenses/MIT) */ #include "Component/GDMSaveSystemComponent.h" #include "GameDebugMenuManager.h" #include "GameDebugMenuSettings.h" #include "Component/GDMPropertyJsonSystemComponent.h" #include "Kismet/GameplayStatics.h" UGDMSaveSystemComponent::UGDMSaveSystemComponent(const FObjectInitializer& ObjectInitializer) : Super(ObjectInitializer) , UserIndex(0) , SaveGame(nullptr) { PrimaryComponentTick.bCanEverTick = false; PrimaryComponentTick.bStartWithTickEnabled = false; } void UGDMSaveSystemComponent::SaveDebugMenuFile() { if (GetDefault()->bDisableSaveFile) { return; } AGameDebugMenuManager* Manager = Cast(GetOwner()); if (!IsValid(Manager)) { UE_LOG(LogGDM, Error, TEXT("SaveDebugMenuFile: GameDebugMenuManager not found on owner actor.")); return; } UGDMPropertyJsonSystemComponent* JsonSystemComponent = GetPropertyJsonSystemComponent(); if (!IsValid(JsonSystemComponent)) { UE_LOG(LogGDM, Error, TEXT("SaveDebugMenuFile: PropertyJsonSystemComponent not found on the same actor.")); return; } TArray CommandHistory; if (!GetDefault()->bDoesNotSaveConsoleCommand) { Manager->GetOutputCommandHistoryString(CommandHistory); } JsonSystemComponent->SetCustomStringArray(TEXT("CommandHistory"), CommandHistory); const FString JsonString = JsonSystemComponent->GetJsonAsString(); if (JsonString.IsEmpty()) { UE_LOG(LogGDM, Log, TEXT("SaveDebugMenuFile: JsonString is empty.")); return; } if (SaveFile(JsonString)) { Manager->CallSavedDebugMenuDispatcher(); } } void UGDMSaveSystemComponent::LoadDebugMenuFile() { if (GetDefault()->bDisableSaveFile) { return; } AGameDebugMenuManager* Manager = Cast(GetOwner()); if (!IsValid(Manager)) { UE_LOG(LogGDM, Error, TEXT("LoadDebugMenuFile: GameDebugMenuManager not found on owner actor.")); return; } UGDMPropertyJsonSystemComponent* JsonSystemComponent = GetPropertyJsonSystemComponent(); if (!IsValid(JsonSystemComponent)) { UE_LOG(LogGDM, Error, TEXT("LoadDebugMenuFile: JsonSystemComponent not found on the same actor.")); return; } FString LoadedJsonString; if (!LoadFile(LoadedJsonString)) { return; } if (!JsonSystemComponent->BuildJsonFromString(LoadedJsonString)) { UE_LOG(LogGDM, Warning, TEXT("LoadDebugMenuFile: Failed to apply JSON to JsonSystemComponent.")); /* ファイルがないため現状の状態を1度保存する */ SaveDebugMenuFile(); return; } Manager->CallLoadedDebugMenuDispatcher(); UE_LOG(LogGDM, Log, TEXT("LoadDebugMenuFile: JSON loaded and applied to JsonSystemComponent.")); } void UGDMSaveSystemComponent::DeleteDebugMenuFile() { AGameDebugMenuManager* Manager = Cast(GetOwner()); if (!IsValid(Manager)) { UE_LOG(LogGDM, Error, TEXT("DeleteDebugMenuFile: GameDebugMenuManager not found on owner actor.")); return; } if (!DeleteFile()) { return; } Manager->CallDeletedDebugMenuDispatcher(); UE_LOG(LogGDM, Log, TEXT("DeleteDebugMenuFile: JSON deleted and applied to JsonSystemComponent.")); } UGDMPropertyJsonSystemComponent* UGDMSaveSystemComponent::GetPropertyJsonSystemComponent() const { if (const AActor* Owner = GetOwner()) { return Owner->FindComponentByClass(); } UE_LOG(LogGDM, Warning, TEXT("GetPropertyJsonSystemComponent: Owner is null or component not found.")); return nullptr; } bool UGDMSaveSystemComponent::SaveFile(const FString& ContentString) { if (CanUseSaveGame()) { if (!IsValid(SaveGame)) { SaveGame = Cast(UGameplayStatics::CreateSaveGameObject(UGDMSaveGame::StaticClass())); } SaveGame->Json = ContentString; const FString SlotName = GetDefault()->SaveFileName; UGameplayStatics::SaveGameToSlot(SaveGame, SlotName, UserIndex); UE_LOG(LogGDM, Log, TEXT("SaveFile: JSON saved to SlotName '%s' UserIndex '%d'"), *SlotName, UserIndex); return true; } else { const FString SaveFilePath = GetDefault()->GetFullSavePath(); if (FFileHelper::SaveStringToFile(ContentString, *SaveFilePath)) { UE_LOG(LogGDM, Log, TEXT("SaveFile: JSON saved to '%s'"), *SaveFilePath); return true; } UE_LOG(LogGDM, Verbose, TEXT("SaveFile: Failed to save JSON to '%s'"), *SaveFilePath); } return false; } bool UGDMSaveSystemComponent::LoadFile(FString& OutLoadedContentString) { OutLoadedContentString.Reset(); if (CanUseSaveGame()) { const FString SlotName = GetDefault()->SaveFileName; SaveGame = Cast(UGameplayStatics::LoadGameFromSlot(SlotName, UserIndex)); if (!IsValid(SaveGame)) { SaveGame = Cast(UGameplayStatics::CreateSaveGameObject(UGDMSaveGame::StaticClass())); } else { UE_LOG(LogGDM, Log, TEXT("LoadFile: JSON loaded to SlotName '%s' UserIndex '%d'"), *SlotName, UserIndex); } OutLoadedContentString = SaveGame->Json; return true; } else { const FString LoadFilePath = GetDefault()->GetFullSavePath(); if (FFileHelper::LoadFileToString(OutLoadedContentString, *LoadFilePath)) { UE_LOG(LogGDM, Log, TEXT("LoadFile: JSON loaded to '%s'"), *LoadFilePath); return true; } UE_LOG(LogGDM, Verbose, TEXT("LoadFile: Failed to load JSON to '%s'"), *LoadFilePath); } return false; } bool UGDMSaveSystemComponent::DeleteFile() { if (CanUseSaveGame()) { const FString SlotName = GetDefault()->SaveFileName; if (UGameplayStatics::DeleteGameInSlot(SlotName, UserIndex)) { return true; } UE_LOG(LogGDM, Warning, TEXT("DeleteFile: Failed to DeleteGameInSlot to '%s'"), *SlotName); } else { const FString FilePath = GetDefault()->GetFullSavePath(); if (FFileHelper::SaveStringToFile(FString(), *FilePath))/* ファイル削除がないので空で上書き */ { UE_LOG(LogGDM, Log, TEXT("DeleteFile: JSON saved to '%s'"), *FilePath); return true; } UE_LOG(LogGDM, Error, TEXT("DeleteFile: Failed to save JSON to '%s'"), *FilePath); } return false; } bool UGDMSaveSystemComponent::CanUseSaveGame() { if (!GetDefault()->bUseSaveGame) { /* テキストファイルで直接書き込み */ #if (PLATFORM_WINDOWS || PLATFORM_MAC) /* 他のプラットフォームだとテキストが扱えないかもしれないため PC のみ */ return false; #endif } /* データはSaveGameを使用 */ return true; } ``` `SampleProject/Plugins/GameDebugMenu/Source/GameDebugMenu/Private/Component/GDMScreenshotRequesterComponent.cpp`: ```cpp /** * Copyright (c) 2020 akihiko moroi * * This software is released under the MIT License. * (See accompanying file LICENSE.txt or copy at http://opensource.org/licenses/MIT) */ #include "Component/GDMScreenshotRequesterComponent.h" #include "Engine/GameViewportClient.h" #include "UnrealClient.h" #include "TimerManager.h" #include "GameDebugMenuManager.h" #include "GameDebugMenuFunctions.h" #include "Widgets/GDMDebugReportWidget.h" UGDMScreenshotRequesterComponent::UGDMScreenshotRequesterComponent(const FObjectInitializer& ObjectInitializer) : Super(ObjectInitializer) , OnScreenshotCapturedHandle() , OnScreenshotRequestProcessedHandle() , bRequestProcessed(true) { PrimaryComponentTick.bCanEverTick = false; PrimaryComponentTick.bStartWithTickEnabled = false; } void UGDMScreenshotRequesterComponent::BeginPlay() { Super::BeginPlay(); } void UGDMScreenshotRequesterComponent::EndPlay(const EEndPlayReason::Type EndPlayReason) { Super::EndPlay(EndPlayReason); ResetHandle(); } void UGDMScreenshotRequesterComponent::RequestScreenshot() { ResetHandle(); bRequestProcessed = false; OnScreenshotCapturedHandle = UGameViewportClient::OnScreenshotCaptured().AddUObject(this, &UGDMScreenshotRequesterComponent::OnScreenshotCaptured); OnScreenshotRequestProcessedHandle = FScreenshotRequest::OnScreenshotRequestProcessed().AddUObject(this, &UGDMScreenshotRequesterComponent::OnScreenshotRequestProcessed); GetOwnerGameDebugMenuManager()->CallStartScreenshotRequestDispatcher(); /* ↑実際のキャプチャ処理は1フレーム後 */ GetWorld()->GetTimerManager().SetTimerForNextTick(FTimerDelegate::CreateLambda([this]() { /* スクショ開始 */ FScreenshotRequest::Reset(); FScreenshotRequest::RequestScreenshot(true); })); } bool UGDMScreenshotRequesterComponent::IsRequestProcessed() { return bRequestProcessed; } AGameDebugMenuManager* UGDMScreenshotRequesterComponent::GetOwnerGameDebugMenuManager() const { return Cast(GetOwner()); } void UGDMScreenshotRequesterComponent::OnScreenshotCaptured(int32 Width, int32 Height, const TArray& Bitmap) { UGameDebugMenuFunctions::PrintLogScreen(this, TEXT("OnScreenshotCaptured"), 4.0f); if(AGameDebugMenuManager* DebugMenuManager = GetOwnerGameDebugMenuManager()) { TArray Widgets; DebugMenuManager->GetDebugMenuWidgetInstances(Widgets); for(const auto Widget : Widgets) { /* Debugレポート用Widgetがあれば通知してあげる */ if(UGDMDebugReportWidget* ReportWidget = Cast(Widget)) { ReportWidget->OnScreenshotCaptured(Width, Height, Bitmap); } } } } void UGDMScreenshotRequesterComponent::OnScreenshotRequestProcessed() { ResetHandle(); UGameDebugMenuFunctions::PrintLogScreen(this, TEXT("OnScreenshotRequestProcessed"), 4.0f); bRequestProcessed = true; GetOwnerGameDebugMenuManager()->CallScreenshotRequestProcessedDispatcher(); } void UGDMScreenshotRequesterComponent::ResetHandle() { UGameViewportClient::OnScreenshotCaptured().Remove(OnScreenshotCapturedHandle); FScreenshotRequest::OnScreenshotRequestProcessed().Remove(OnScreenshotRequestProcessedHandle); OnScreenshotCapturedHandle.Reset(); OnScreenshotRequestProcessedHandle.Reset(); } ``` `SampleProject/Plugins/GameDebugMenu/Source/GameDebugMenu/Private/ConsoleCommand/GDMConsoleCommandValueProvider.cpp`: ```cpp /** * Copyright (c) 2020 akihiko moroi * * This software is released under the MIT License. * (See accompanying file LICENSE.txt or copy at http://opensource.org/licenses/MIT) */ #include "ConsoleCommand/GDMConsoleCommandValueProvider.h" ``` `SampleProject/Plugins/GameDebugMenu/Source/GameDebugMenu/Private/ConsoleCommand/GDMConsoleCommandValueProviderComponent.cpp`: ```cpp /** * Copyright (c) 2020 akihiko moroi * * This software is released under the MIT License. * (See accompanying file LICENSE.txt or copy at http://opensource.org/licenses/MIT) */ #include "ConsoleCommand/GDMConsoleCommandValueProviderComponent.h" #include "ConsoleCommand/GDMConsoleVariableCommandValueProvider.h" UGDMConsoleCommandValueProviderComponent::UGDMConsoleCommandValueProviderComponent() { PrimaryComponentTick.bCanEverTick = false; PrimaryComponentTick.bStartWithTickEnabled = false; } bool UGDMConsoleCommandValueProviderComponent::GetFloatValue(const FString& CommandName, float& OutValue) { for (const FGDMConsoleCommandProviderPattern& PatternStruct : ProviderPatterns) { if (PatternStruct.Pattern.IsEmpty() || !IsValid(PatternStruct.Provider)) { continue; } if (CommandName.Contains(PatternStruct.Pattern)) { return PatternStruct.Provider->GetFloatValue(CommandName, OutValue); } } if (IsValid(ConsoleVariableCommandValueProvider)) { return ConsoleVariableCommandValueProvider->GetFloatValue(CommandName, OutValue); } return false; } ``` `SampleProject/Plugins/GameDebugMenu/Source/GameDebugMenu/Private/ConsoleCommand/GDMConsoleVariableCommandValueProvider.cpp`: ```cpp /** * Copyright (c) 2020 akihiko moroi * * This software is released under the MIT License. * (See accompanying file LICENSE.txt or copy at http://opensource.org/licenses/MIT) */ #include "ConsoleCommand/GDMConsoleVariableCommandValueProvider.h" bool UGDMConsoleVariableCommandValueProvider::GetFloatValue_Implementation(const FString& CommandName, float& OutValue) const { if (const IConsoleVariable* Var = IConsoleManager::Get().FindConsoleVariable(*CommandName)) { OutValue = Var->GetFloat(); return true; } return false; } ``` `SampleProject/Plugins/GameDebugMenu/Source/GameDebugMenu/Private/ConsoleCommand/GDMSlomoCommandValueProvider.cpp`: ```cpp /** * Copyright (c) 2020 akihiko moroi * * This software is released under the MIT License. * (See accompanying file LICENSE.txt or copy at http://opensource.org/licenses/MIT) */ #include "ConsoleCommand/GDMSlomoCommandValueProvider.h" bool UGDMSlomoCommandValueProvider::GetFloatValue_Implementation(const FString& CommandName, float& OutValue) const { if (const UWorld* World = GetWorld()) { if (IsValid(World->GetWorldSettings())) { OutValue = World->GetWorldSettings()->GetEffectiveTimeDilation(); return true; } } return false; } ``` `SampleProject/Plugins/GameDebugMenu/Source/GameDebugMenu/Private/Data/GDMConsoleCommandSetAsset.cpp`: ```cpp /** * Copyright (c) 2020 akihiko moroi * * This software is released under the MIT License. * (See accompanying file LICENSE.txt or copy at http://opensource.org/licenses/MIT) */ #include "Data/GDMConsoleCommandSetAsset.h" #include "GameDebugMenuSettings.h" void UGDMConsoleCommandSetAsset::SetupCommandNames() { #if WITH_EDITORONLY_DATA const UGameDebugMenuSettings* Settings = GetDefault(); auto FilterByCategory = [this](const auto& CommandArray, auto& OutArray) { OutArray.Reset(); if (CategoryIndexList.IsEmpty()) { for (const auto& Item : CommandArray) { OutArray.Add(Item); } } else { for (const auto& Item : CommandArray) { if (CategoryIndexList.Contains(Item.CategoryIndex)) { OutArray.Add(Item); } } } }; FilterByCategory(Settings->ConsoleCommandNames, ConsoleCommandNames); FilterByCategory(Settings->ConsoleCommandGroups, ConsoleCommandGroups); FilterByCategory(Settings->ConsoleCommandPairs, ConsoleCommandPairs); FilterByCategory(Settings->ConsoleCommandNumbers, ConsoleCommandNumbers); FilterByCategory(Settings->EditorOnlyConsoleCommandNames, EditorOnlyConsoleCommandNames); FilterByCategory(Settings->EditorOnlyConsoleCommandGroups, EditorOnlyConsoleCommandGroups); FilterByCategory(Settings->EditorOnlyConsoleCommandPairs, EditorOnlyConsoleCommandPairs); FilterByCategory(Settings->EditorOnlyConsoleCommandNumbers, EditorOnlyConsoleCommandNumbers); this->Modify(); #endif } void UGDMConsoleCommandSetAsset::SetupOrderConsoleCommandCategoryTitles() { #if WITH_EDITORONLY_DATA const UGameDebugMenuSettings* Settings = GetDefault(); OrderConsoleCommandCategoryTitles = Settings->OrderConsoleCommandCategoryTitles; this->Modify(); #endif } void UGDMConsoleCommandSetAsset::MergeFromSourceAssets() { #if WITH_EDITORONLY_DATA auto MergeCommands = [](auto& Target, const auto& Source) { for (const auto& Item : Source) { const FString NewId = Item.BuildCommandIdentifier(); if (!Target.ContainsByPredicate([&](const auto& Existing) { return Existing.BuildCommandIdentifier() == NewId; })) { Target.Add(Item); } } }; for (const UGDMConsoleCommandSetAsset* SourceAsset : MergeSourceAssets) { if (!IsValid(SourceAsset)) { continue; } MergeCommands(ConsoleCommandNames, SourceAsset->ConsoleCommandNames); MergeCommands(ConsoleCommandGroups, SourceAsset->ConsoleCommandGroups); MergeCommands(ConsoleCommandPairs, SourceAsset->ConsoleCommandPairs); MergeCommands(ConsoleCommandNumbers, SourceAsset->ConsoleCommandNumbers); MergeCommands(EditorOnlyConsoleCommandNames, SourceAsset->EditorOnlyConsoleCommandNames); MergeCommands(EditorOnlyConsoleCommandGroups, SourceAsset->EditorOnlyConsoleCommandGroups); MergeCommands(EditorOnlyConsoleCommandPairs, SourceAsset->EditorOnlyConsoleCommandPairs); MergeCommands(EditorOnlyConsoleCommandNumbers, SourceAsset->EditorOnlyConsoleCommandNumbers); } this->Modify(); #endif } TArray UGDMConsoleCommandSetAsset::GetOrderConsoleCommandCategoryTitle() const { TArray ReturnValues; const auto& TitleArray = OrderConsoleCommandCategoryTitles; for( int32 Index = 0;Index < TitleArray.Num();++Index ) { ReturnValues.Add(FGDMMenuCategoryKey(TitleArray[Index].Index, TitleArray[Index].Title)); } return ReturnValues; } bool UGDMConsoleCommandSetAsset::GetConsoleCommandNameByArrayIndex(const int32 ArrayIndex, FGDMConsoleCommandSingle& Out) const { #if WITH_EDITOR TArray Commands; Commands.Append(ConsoleCommandNames); Commands.Append(EditorOnlyConsoleCommandNames); if (!Commands.IsValidIndex(ArrayIndex)) { return false; } Out = Commands[ArrayIndex]; #else const auto& Commands = ConsoleCommandNames; if (!Commands.IsValidIndex(ArrayIndex)) { return false; } Out = Commands[ArrayIndex]; #endif return true; } int32 UGDMConsoleCommandSetAsset::GetNumConsoleCommandNames() const { #if WITH_EDITOR TArray Commands; Commands.Append(ConsoleCommandNames); Commands.Append(EditorOnlyConsoleCommandNames); return Commands.Num(); #else return ConsoleCommandNames.Num(); #endif } bool UGDMConsoleCommandSetAsset::GetConsoleCommandGroupByArrayIndex(const int32 ArrayIndex, FGDMConsoleCommandGroup& Out) const { #if WITH_EDITOR TArray Commands; Commands.Append(ConsoleCommandGroups); Commands.Append(EditorOnlyConsoleCommandGroups); if (!Commands.IsValidIndex(ArrayIndex)) { return false; } Out = Commands[ArrayIndex]; #else const auto& Commands = ConsoleCommandGroups; if (!Commands.IsValidIndex(ArrayIndex)) { return false; } Out = Commands[ArrayIndex]; #endif return true; } int32 UGDMConsoleCommandSetAsset::GetNumConsoleCommandGroups() const { #if WITH_EDITOR TArray Commands; Commands.Append(ConsoleCommandGroups); Commands.Append(EditorOnlyConsoleCommandGroups); return Commands.Num(); #else return ConsoleCommandGroups.Num(); #endif } bool UGDMConsoleCommandSetAsset::GetConsoleCommandPairByArrayIndex(const int32 ArrayIndex, FGDMConsoleCommandPair& Out) const { #if WITH_EDITOR TArray Commands; Commands.Append(ConsoleCommandPairs); Commands.Append(EditorOnlyConsoleCommandPairs); if (!Commands.IsValidIndex(ArrayIndex)) { return false; } Out = Commands[ArrayIndex]; #else const auto& Commands = ConsoleCommandPairs; if (!Commands.IsValidIndex(ArrayIndex)) { return false; } Out = Commands[ArrayIndex]; #endif return true; } int32 UGDMConsoleCommandSetAsset::GetNumConsoleCommandPairs() const { #if WITH_EDITOR TArray Commands; Commands.Append(ConsoleCommandPairs); Commands.Append(EditorOnlyConsoleCommandPairs); return Commands.Num(); #else return ConsoleCommandPairs.Num(); #endif } bool UGDMConsoleCommandSetAsset::GetConsoleCommandNumberByArrayIndex(const int32 ArrayIndex, FGDMConsoleCommandNumber& Out) const { #if WITH_EDITOR TArray Commands; Commands.Append(ConsoleCommandNumbers); Commands.Append(EditorOnlyConsoleCommandNumbers); if (!Commands.IsValidIndex(ArrayIndex)) { return false; } Out = Commands[ArrayIndex]; #else const auto& Commands = ConsoleCommandNumbers; if (!Commands.IsValidIndex(ArrayIndex)) { return false; } Out = Commands[ArrayIndex]; #endif return true; } int32 UGDMConsoleCommandSetAsset::GetNumConsoleCommandNumbers() const { #if WITH_EDITOR TArray Commands; Commands.Append(ConsoleCommandNumbers); Commands.Append(EditorOnlyConsoleCommandNumbers); return Commands.Num(); #else return ConsoleCommandNumbers.Num(); #endif } bool UGDMConsoleCommandSetAsset::FindConsoleCommandSingleById(const FString& CommandId, FGDMConsoleCommandSingle& Out) const { for (const FGDMConsoleCommandSingle& Cmd : ConsoleCommandNames) { if (Cmd.BuildCommandIdentifier() == CommandId) { Out = Cmd; return true; } } #if WITH_EDITOR for (const FGDMConsoleCommandSingle& Cmd : EditorOnlyConsoleCommandNames) { if (Cmd.BuildCommandIdentifier() == CommandId) { Out = Cmd; return true; } } #endif return false; } bool UGDMConsoleCommandSetAsset::FindConsoleCommandGroupById(const FString& CommandId, FGDMConsoleCommandGroup& Out) const { for (const FGDMConsoleCommandGroup& Cmd : ConsoleCommandGroups) { if (Cmd.BuildCommandIdentifier() == CommandId) { Out = Cmd; return true; } } #if WITH_EDITOR for (const FGDMConsoleCommandGroup& Cmd : EditorOnlyConsoleCommandGroups) { if (Cmd.BuildCommandIdentifier() == CommandId) { Out = Cmd; return true; } } #endif return false; } bool UGDMConsoleCommandSetAsset::FindConsoleCommandPairById(const FString& CommandId, FGDMConsoleCommandPair& Out) const { for (const FGDMConsoleCommandPair& Cmd : ConsoleCommandPairs) { if (Cmd.BuildCommandIdentifier() == CommandId) { Out = Cmd; return true; } } #if WITH_EDITOR for (const FGDMConsoleCommandPair& Cmd : EditorOnlyConsoleCommandPairs) { if (Cmd.BuildCommandIdentifier() == CommandId) { Out = Cmd; return true; } } #endif return false; } bool UGDMConsoleCommandSetAsset::FindConsoleCommandNumberById(const FString& CommandId, FGDMConsoleCommandNumber& Out) const { for (const FGDMConsoleCommandNumber& Cmd : ConsoleCommandNumbers) { if (Cmd.BuildCommandIdentifier() == CommandId) { Out = Cmd; return true; } } #if WITH_EDITOR for (const FGDMConsoleCommandNumber& Cmd : EditorOnlyConsoleCommandNumbers) { if (Cmd.BuildCommandIdentifier() == CommandId) { Out = Cmd; return true; } } #endif return false; } ``` `SampleProject/Plugins/GameDebugMenu/Source/GameDebugMenu/Private/Data/GameDebugMenuManagerAsset.cpp`: ```cpp /** * Copyright (c) 2020 akihiko moroi * * This software is released under the MIT License. * (See accompanying file LICENSE.txt or copy at http://opensource.org/licenses/MIT) */ #include "Data/GameDebugMenuManagerAsset.h" #include "Component/GDMPlayerControllerProxyComponent.h" #include "Input/GDMDebugCameraInput.h" UGameDebugMenuManagerAsset::UGameDebugMenuManagerAsset() : Super() , DebugMenuRootWidgetClass() , DebugMenuClasses() , DebugMenuRegistrationOrder() , RootWidgetZOrder(TNumericLimits::Max() - 100)/* モバイルとかの仮想パッドより上になるよう強制で10加算されるので問題内容100小さくしてる。UGameViewportSubsystem::AddToScreen参照 */ , AddInputMappingContextWhenCreateManager() , AddInputMappingContextWhenDebugMenuIsShow() , DebugCameraInputClass(nullptr) , FavoriteItemDefinitions() , DebugMenuPCProxyComponentClass() , bGamePause(false) { DebugMenuPCProxyComponentClass = UGDMPlayerControllerProxyComponent::StaticClass(); DebugCameraInputClass = AGDMDebugCameraInput::StaticClass(); } ``` `SampleProject/Plugins/GameDebugMenu/Source/GameDebugMenu/Private/Data/GameDebugMenuMasterAsset.cpp`: ```cpp /** * Copyright (c) 2020 akihiko moroi * * This software is released under the MIT License. * (See accompanying file LICENSE.txt or copy at http://opensource.org/licenses/MIT) */ #include "Data/GameDebugMenuMasterAsset.h" UGameDebugMenuMasterAsset::UGameDebugMenuMasterAsset(const FObjectInitializer& ObjectInitializer) : Super(ObjectInitializer) , DebugMenuManagerClasses() , DebugMenuInputComponentClass(nullptr) , DebugReportRequesterClass() , GameDebugMenuStringTables() , FontName(nullptr) { } FPrimaryAssetId UGameDebugMenuMasterAsset::GetPrimaryAssetId() const { return FPrimaryAssetId(GetPrimaryType(), GetFName()); } const FPrimaryAssetType& UGameDebugMenuMasterAsset::GetPrimaryType() { static const FPrimaryAssetType AssetType = TEXT("GameDebugMenuMaster"); return AssetType; } TSoftClassPtr UGameDebugMenuMasterAsset::GetGameDebugMenuManagerSoftClass(FString ClassName) const { for (const TSoftClassPtr& ClassPtr : DebugMenuManagerClasses) { if (ClassPtr.IsNull()) { continue; } FString AssetName = ClassPtr.GetAssetName(); AssetName.RemoveFromEnd(TEXT("_C")); if (AssetName == ClassName) { return ClassPtr; } } return nullptr; } ``` `SampleProject/Plugins/GameDebugMenu/Source/GameDebugMenu/Private/Favorite/GDMFavoriteItemDefinition.cpp`: ```cpp /** * Copyright (c) 2020 akihiko moroi * * This software is released under the MIT License. * (See accompanying file LICENSE.txt or copy at http://opensource.org/licenses/MIT) */ #include "Favorite/GDMFavoriteItemDefinition.h" #include "GameDebugMenuManager.h" UWorld* UGDMFavoriteItemDefinition::GetWorld() const { if (UPackage* PkgObj = Cast(GetOuter())) { return GWorld; } if (OwnerManager.IsValid()) { return OwnerManager.Get()->GetWorld(); } return nullptr; } AGameDebugMenuManager* UGDMFavoriteItemDefinition::GetOwnerManager() const { if (OwnerManager.IsValid()) { return OwnerManager.Get(); } return nullptr; } ``` `SampleProject/Plugins/GameDebugMenu/Source/GameDebugMenu/Private/Favorite/GDMFavoriteSystemComponent.cpp`: ```cpp /** * Copyright (c) 2020 akihiko moroi * * This software is released under the MIT License. * (See accompanying file LICENSE.txt or copy at http://opensource.org/licenses/MIT) */ #include "Favorite/GDMFavoriteSystemComponent.h" #include "GameDebugMenuManager.h" #include "GameDebugMenuTypes.h" #include "Blueprint/UserWidget.h" #include "Component/GDMPropertyJsonSystemComponent.h" #include "Data/GameDebugMenuManagerAsset.h" #include "Favorite/GDMFavoriteItemDefinition.h" UGDMFavoriteSystemComponent::UGDMFavoriteSystemComponent() { PrimaryComponentTick.bCanEverTick = false; PrimaryComponentTick.bStartWithTickEnabled = false; } UGDMPropertyJsonSystemComponent* UGDMFavoriteSystemComponent::GetPropertyJsonSystemComponent() const { if (const AActor* Owner = GetOwner()) { return Owner->FindComponentByClass(); } UE_LOG(LogGDM, Warning, TEXT("GetPropertyJsonSystemComponent: Owner is null or component not found.")); return nullptr; } void UGDMFavoriteSystemComponent::Initialize(const UGameDebugMenuManagerAsset* InMenuAsset) { if (!IsValid(InMenuAsset)) { UE_LOG(LogGDM, Warning, TEXT("UGDMFavoriteSystemComponent::Initialize: Invalid MenuDataAsset.")); return; } CachedFavoriteDefinitions = InMenuAsset->FavoriteItemDefinitions; for (auto& Def : CachedFavoriteDefinitions) { Def->OwnerManager = Cast(GetOwner()); } } bool UGDMFavoriteSystemComponent::ToggleAddOrRemoveFavorite(UUserWidget* TargetWidget) { if (CanFavorite(TargetWidget)) { if (IsFavorited(TargetWidget)) { RemoveFavorite(TargetWidget); } else { AddFavorite(TargetWidget); } return true; } return false; } void UGDMFavoriteSystemComponent::AddFavorite(UUserWidget* TargetWidget) { if (!IsValid(TargetWidget)) { UE_LOG(LogGDM, Warning, TEXT("AddFavorite: Invalid target or MenuDataAsset.")); return; } UGDMPropertyJsonSystemComponent* JsonSystem = GetPropertyJsonSystemComponent(); if (!IsValid(JsonSystem)) { UE_LOG(LogGDM, Warning, TEXT("AddFavorite: JsonSystem not found.")); return; } for (const auto& Def : CachedFavoriteDefinitions) { if (!IsValid(Def) || !Def->FavoriteWidgetClass) { continue; } if (!TargetWidget->IsA(Def->FavoriteWidgetClass)) { continue; } const FString SaveKey = Def->ExportToFavoriteKey(TargetWidget); if (SaveKey.IsEmpty()) { UE_LOG(LogGDM, Warning, TEXT("AddFavorite: ExportToFavoriteKey failed or returned empty.")); return; } const FString DefinitionName = Def->GetClass()->GetName(); if (JsonSystem->HasFavoriteEntry(DefinitionName, SaveKey)) { UE_LOG(LogGDM, Log, TEXT("AddFavorite: Already exists (%s, %s)"), *DefinitionName, *SaveKey); return; } JsonSystem->AddFavoriteEntry(DefinitionName, SaveKey); UE_LOG(LogGDM, Verbose, TEXT("AddFavorite: Added (%s, %s)"), *DefinitionName, *SaveKey); return; } UE_LOG(LogGDM, Warning, TEXT("AddFavorite: No matching FavoriteItemDefinition found for widget class %s"), *GetNameSafe(TargetWidget->GetClass())); } void UGDMFavoriteSystemComponent::RemoveFavorite(UUserWidget* TargetWidget) { if (!IsValid(TargetWidget)) { return; } UGDMPropertyJsonSystemComponent* JsonSystem = GetPropertyJsonSystemComponent(); if (!IsValid(JsonSystem)) { return; } for (const auto& Def : CachedFavoriteDefinitions) { if (!IsValid(Def) || !TargetWidget->IsA(Def->FavoriteWidgetClass)) { continue; } const FString SaveKey = Def->ExportToFavoriteKey(TargetWidget); if (SaveKey.IsEmpty()) { continue; } const FString DefinitionName = Def->GetClass()->GetName(); JsonSystem->RemoveFavoriteEntry(DefinitionName, SaveKey); return; } } bool UGDMFavoriteSystemComponent::CanFavorite(UUserWidget* TargetWidget) { if (!IsValid(TargetWidget)) { UE_LOG(LogGDM, Warning, TEXT("AddFavorite: TargetWidget is invalid.")); return false; } for (const auto& Def : CachedFavoriteDefinitions) { if (!IsValid(Def) || !Def->FavoriteWidgetClass) { continue; } if (!TargetWidget->IsA(Def->FavoriteWidgetClass)) { continue; } if (!Def->IsSupportedWidget(TargetWidget)) { continue; } /* お気に入り指定可能 */ return true; } return false; } bool UGDMFavoriteSystemComponent::IsFavorited(UUserWidget* TargetWidget) { if (!IsValid(TargetWidget)) { return false; } UGDMPropertyJsonSystemComponent* JsonSystem = GetPropertyJsonSystemComponent(); if (!IsValid(JsonSystem)) { return false; } for (const auto& Def : CachedFavoriteDefinitions) { if (!IsValid(Def) || !TargetWidget->IsA(Def->FavoriteWidgetClass)) { continue; } const FString SaveKey = Def->ExportToFavoriteKey(TargetWidget); if (SaveKey.IsEmpty()) { continue; } const FString DefinitionName = Def->GetClass()->GetName(); if (JsonSystem->HasFavoriteEntry(DefinitionName, SaveKey)) { return true; } } return false; } void UGDMFavoriteSystemComponent::ClearAllFavorites() { if (UGDMPropertyJsonSystemComponent* JsonSystem = GetPropertyJsonSystemComponent()) { const TArray All = JsonSystem->GetAllFavoriteEntries(); for (const FGDMFavoriteEntry& Entry : All) { JsonSystem->RemoveFavoriteEntry(Entry.DefinitionName, Entry.SaveKey); } } } bool UGDMFavoriteSystemComponent::IsFavoriteListEqualTo(const TArray& OtherList) const { if (const UGDMPropertyJsonSystemComponent* JsonSystem = GetPropertyJsonSystemComponent()) { const TArray Current = JsonSystem->GetAllFavoriteEntries(); if (Current.Num() != OtherList.Num()) { /* 件数が異なるのでNG */ return false; } for (int32 i = 0; i < Current.Num(); ++i) { if (Current[i].DefinitionName != OtherList[i].DefinitionName || Current[i].SaveKey != OtherList[i].SaveKey) { /* いずれかのデータが合わないのでNG */ return false; } } return true; } /* JSONが取得できなければ常に差異ありとみなす */ return false; } UUserWidget* UGDMFavoriteSystemComponent::CreateFavoriteWidgetFromEntry(const FGDMFavoriteEntry& Entry) { if (UGDMPropertyJsonSystemComponent* JsonSystem = GetPropertyJsonSystemComponent()) { for (UGDMFavoriteItemDefinition* Def : CachedFavoriteDefinitions) { if (IsValid(Def) && Def->GetClass()->GetName() == Entry.DefinitionName) { return Def->CreateWidgetFromFavoriteData(Entry.SaveKey); } } } UE_LOG(LogGDM, Warning, TEXT("CreateFavoriteWidgetFromEntry: Definition not found: %s"), *Entry.DefinitionName); return nullptr; } ``` `SampleProject/Plugins/GameDebugMenu/Source/GameDebugMenu/Private/GameDebugMenu.cpp`: ```cpp /** * Copyright (c) 2020 akihiko moroi * * This software is released under the MIT License. * (See accompanying file LICENSE.txt or copy at http://opensource.org/licenses/MIT) */ #include "GameDebugMenu.h" #define LOCTEXT_NAMESPACE "FGameDebugMenuModule" void FGameDebugMenuModule::StartupModule() { UE_LOG(LogTemp, Display, TEXT("FGameDebugMenuModule StartupModule")); } void FGameDebugMenuModule::ShutdownModule() { UE_LOG(LogTemp, Display, TEXT("FGameDebugMenuModule ShutdownModule")); } #undef LOCTEXT_NAMESPACE IMPLEMENT_MODULE(FGameDebugMenuModule, GameDebugMenu) ``` `SampleProject/Plugins/GameDebugMenu/Source/GameDebugMenu/Private/GameDebugMenuFunctions.cpp`: ```cpp /** * Copyright (c) 2020 akihiko moroi * * This software is released under the MIT License. * (See accompanying file LICENSE.txt or copy at http://opensource.org/licenses/MIT) */ #include "GameDebugMenuFunctions.h" #include "GameFramework/WorldSettings.h" #include "Engine/Engine.h" #include "Kismet/GameplayStatics.h" #include "CoreGlobals.h" #include "Misc/ConfigCacheIni.h" #include #include "GameDebugMenuSettings.h" #include "Component/GDMLocalizeStringComponent.h" #include "Data/GameDebugMenuMasterAsset.h" #include "Input/GDMInputSystemComponent.h" TArray< TWeakObjectPtr > GGameDebugMenuManagers; TArray UGameDebugMenuFunctions::RegisterPendingProperties; TArray UGameDebugMenuFunctions::RegisterPendingFunctions; FDelegateHandle UGameDebugMenuFunctions::ActorSpawnedDelegateHandle; bool UGameDebugMenuFunctions::bDisableGameDebugMenu = false; UGameDebugMenuFunctions::UGameDebugMenuFunctions(const FObjectInitializer& ObjectInitializer) : Super(ObjectInitializer) { if(!IsRunningCommandlet()) { auto& ConsoleManager = IConsoleManager::Get(); const FString ShowCommand = TEXT("GDM.Show"); const FString HideCommand = TEXT("GDM.Hide"); const FString ToggleMenuCommand = TEXT("GDM.ToggleMenu"); const FString ToggleInputCommand = TEXT("GDM.ToggleInput"); if(ConsoleManager.FindConsoleObject(*ShowCommand) == nullptr) { ConsoleManager.RegisterConsoleCommand(*ShowCommand, TEXT("Show Game Debug Menu"), FConsoleCommandDelegate::CreateStatic(UGameDebugMenuFunctions::ShowDebugConsoleCommand), ECVF_Default); } if(ConsoleManager.FindConsoleObject(*HideCommand) == nullptr) { ConsoleManager.RegisterConsoleCommand(*HideCommand, TEXT("Hide Game Debug Menu"), FConsoleCommandDelegate::CreateStatic(UGameDebugMenuFunctions::HideDebugConsoleCommand), ECVF_Default); } if(ConsoleManager.FindConsoleObject(*ToggleMenuCommand) == nullptr) { ConsoleManager.RegisterConsoleCommand(*ToggleMenuCommand, TEXT("Toggle Game Debug Menu"), FConsoleCommandDelegate::CreateStatic(UGameDebugMenuFunctions::ToggleDebugConsoleCommand), ECVF_Default); } if( ConsoleManager.FindConsoleObject(*ToggleInputCommand) == nullptr ) { ConsoleManager.RegisterConsoleCommand(*ToggleInputCommand, TEXT("Toggle Game Debug Menu InputSystem Log"), FConsoleCommandDelegate::CreateStatic(UGameDebugMenuFunctions::ToggleInputSystemLog), ECVF_Default); } } } void UGameDebugMenuFunctions::RegisterGameDebugMenuManagerInstance(AGameDebugMenuManager* RegisterManager) { GGameDebugMenuManagers.AddUnique(RegisterManager); if( !UKismetSystemLibrary::IsServer(RegisterManager) ) { /* Client */ OnActorSpawnedClientWaitManager(RegisterManager); } } void UGameDebugMenuFunctions::UnregisterGameDebugMenuManagerInstance(AGameDebugMenuManager* UnregisterManager) { GGameDebugMenuManagers.Remove(UnregisterManager); } bool UGameDebugMenuFunctions::TryCreateDebugMenuManager(APlayerController* PlayerController, FString DebugMenuManagerClassName) { bool bServer = UKismetSystemLibrary::IsServer(PlayerController); FString NetMode = (bServer ? TEXT("Server") : TEXT("Client")); UWorld* World = GEngine->GetWorldFromContextObject(PlayerController, EGetWorldErrorMode::LogAndReturnNull); if(World == nullptr) { UE_LOG(LogGDM, Warning, TEXT("TryCreateDebugMenuManager: Not found world[%s]"), *NetMode); return false; } if (DebugMenuManagerClassName.IsEmpty()) { UE_LOG(LogGDM, Warning, TEXT("TryCreateDebugMenuManager: class name empty[%s]"), *NetMode); return false; } const UGameDebugMenuMasterAsset* MasterAsset = GetDefault()->GetMasterAsset(); if (!IsValid(MasterAsset)) { UE_LOG(LogGDM, Warning, TEXT("TryCreateDebugMenuManager: Not found MasterAsset[%s]"), *NetMode); /* 生成ができない環境なので無効フラグを立てる */ bDisableGameDebugMenu = true; RegisterPendingProperties.Empty(); RegisterPendingFunctions.Empty(); return false; } const auto ManagerClass = MasterAsset->GetGameDebugMenuManagerSoftClass(DebugMenuManagerClassName); UClass* DebugMenuManagerClass = ManagerClass.LoadSynchronous(); if(DebugMenuManagerClass == nullptr) { FString ErrorStr = TEXT("DebugMenuManagerClass NotFound LoadError"); ErrorStr += TEXT("["); ErrorStr += NetMode; ErrorStr += TEXT("]"); UE_LOG(LogGDM, Warning, TEXT("TryCreateDebugMenuManager: Failed Class name [%s] %s"), *NetMode, *DebugMenuManagerClassName); UKismetSystemLibrary::PrintString(PlayerController, ErrorStr, true, true, FLinearColor::Red, 10.0f); /* 生成ができない環境なので無効フラグを立てる */ bDisableGameDebugMenu = true; RegisterPendingProperties.Empty(); RegisterPendingFunctions.Empty(); return false; } bDisableGameDebugMenu = false; if(!bServer) { /* Client */ if( !IsValid(GetGameDebugMenuManager(PlayerController, false)) ) { /* まだ生成してもらってないので待つ */ UE_LOG(LogGDM, Log, TEXT("TryCreateDebugMenuManager: Wait spawn[%s]"), *NetMode); return true; } } else { /* Server */ if (IsValid(TryGetGameDebugMenuManagerFromPlayerController(PlayerController))) { UE_LOG(LogGDM, Log, TEXT("TryCreateDebugMenuManager: Created manager[%s]"), *NetMode); return true; } FActorSpawnParameters SpawnParams; SpawnParams.Owner = PlayerController; SpawnParams.SpawnCollisionHandlingOverride = ESpawnActorCollisionHandlingMethod::AlwaysSpawn; AGameDebugMenuManager* Manager = World->SpawnActor(DebugMenuManagerClass, FTransform::Identity, SpawnParams); for (FConstPlayerControllerIterator Iterator = World->GetPlayerControllerIterator(); Iterator; ++Iterator) { APlayerController* PC = Iterator->Get(); if (!IsValid(PC)) { continue; } /* PlayerControllerにコンポーネントを追加 */ Manager->AddDebugMenuPCProxyComponent(PC); } ActorSpawnedDelegateHandle = World->AddOnActorSpawnedHandler(FOnActorSpawned::FDelegate::CreateStatic(&UGameDebugMenuFunctions::OnActorSpawnedServer)); } /* マネージャー生成前に登録処理したプロパティ群を追加する */ for(int32 Index = 0; Index < RegisterPendingProperties.Num(); ++Index) { if(RegisterPendingProperties.IsValidIndex(Index)) { const auto& PendingData = RegisterPendingProperties[Index]; if(PendingData.TargetObject.IsValid()) { RegisterGDMObjectProperty(PendingData.TargetObject.Get() ,PendingData.ConfigInfo ,PendingData.TargetName ,PendingData.CategoryKey ,PendingData.SaveKey ,PendingData.DisplayPropertyName ,PendingData.Description ,PendingData.DisplayPriority ); } } } /* マネージャー生成前に登録処理した関数群を追加する */ for(int32 Index = 0; Index < RegisterPendingFunctions.Num(); ++Index) { if(RegisterPendingFunctions.IsValidIndex(Index)) { const auto& PendingData = RegisterPendingFunctions[Index]; if(PendingData.TargetObject.IsValid()) { RegisterGDMObjectFunction(PendingData.TargetObject.Get(), PendingData.TargetName, PendingData.CategoryKey, PendingData.SaveKey, PendingData.DisplayPropertyName, PendingData.Description, PendingData.DisplayPriority ); } } } RegisterPendingProperties.RemoveAll([&](const FGDMPendingObjectData& PropertyData) { if(!PropertyData.TargetObject.IsValid()) { return true; } return (PropertyData.TargetObject->GetWorld() == World ); }); RegisterPendingFunctions.RemoveAll([&](const FGDMPendingObjectData& FunctionData) { if(!FunctionData.TargetObject.IsValid()) { return true; } return (FunctionData.TargetObject->GetWorld() == World ); }); UE_LOG(LogGDM, Log, TEXT("TryCreateDebugMenuManager: Success %s"), *NetMode); return true; } bool UGameDebugMenuFunctions::DestroyDebugMenuManager(APlayerController* PlayerController) { UWorld* World = GEngine->GetWorldFromContextObject(PlayerController, EGetWorldErrorMode::LogAndReturnNull); if(World != nullptr) { World->RemoveOnActorSpawnedHandler(ActorSpawnedDelegateHandle); RegisterPendingProperties.RemoveAll([&](const FGDMPendingObjectData& PropertyData) { if (!PropertyData.TargetObject.IsValid()) { return true; } return (PropertyData.TargetObject->GetWorld() == World); }); RegisterPendingFunctions.RemoveAll([&](const FGDMPendingObjectData& FunctionData) { if (!FunctionData.TargetObject.IsValid()) { return true; } return (FunctionData.TargetObject->GetWorld() == World); }); } AGameDebugMenuManager* GDMManager = GetGameDebugMenuManager(PlayerController, false); if(IsValid(GDMManager)) { GDMManager->HideDebugMenu(); GDMManager->Destroy(); return true; } return false; } AGameDebugMenuManager* UGameDebugMenuFunctions::GetGameDebugMenuManager(const UObject* WorldContextObject, const bool bCheckInitialize) { if(bDisableGameDebugMenu) { return nullptr; } const UWorld* World = GEngine->GetWorldFromContextObject(WorldContextObject, EGetWorldErrorMode::LogAndReturnNull); if( !IsValid(World) ) { UE_LOG(LogGDM, Warning, TEXT("GetGameDebugMenuManager: not found World")); return nullptr; } AGameDebugMenuManager* GDMManager = nullptr; for (const auto Manager : GGameDebugMenuManagers ) { if (Manager->GetWorld() == World) { if (IsValid(Manager->GetOwner())) { GDMManager = Cast(Manager); break; } } } if (IsValid(GDMManager)) { if(bCheckInitialize) { return GDMManager->IsInitializedManager() ? GDMManager : nullptr; } return GDMManager; } return nullptr; } AGameDebugMenuManager* UGameDebugMenuFunctions::TryGetGameDebugMenuManagerFromPlayerController(const APlayerController* PlayerController) { for (const auto Manager : GGameDebugMenuManagers ) { if (Manager->GetOwner() == PlayerController) { return Cast(Manager); } } return nullptr; } bool UGameDebugMenuFunctions::ShowDebugMenu(UObject* WorldContextObject) { if(bDisableGameDebugMenu) { UE_LOG(LogGDM, Warning, TEXT("ShowDebugMenu: GameDebugMenu is disabled")); return false; } AGameDebugMenuManager* GDMManager = GetGameDebugMenuManager(WorldContextObject); if(!IsValid(GDMManager)) { UE_LOG(LogGDM, Warning, TEXT("ShowDebugMenu: Instance Not Found")); return false; } return GDMManager->ShowDebugMenu(); } bool UGameDebugMenuFunctions::HideDebugMenu(UObject* WorldContextObject) { AGameDebugMenuManager* GDMManager = GetGameDebugMenuManager(WorldContextObject); if(!IsValid(GDMManager)) { UE_LOG(LogGDM, Log, TEXT("HideDebugMenu: Instance Not Found")); return false; } GDMManager->HideDebugMenu(); return true; } bool UGameDebugMenuFunctions::ToggleDebugMenu(UObject* WorldContextObject) { AGameDebugMenuManager* GDMManager = GetGameDebugMenuManager(WorldContextObject); if(!IsValid(GDMManager)) { UE_LOG(LogGDM, Log, TEXT("ToggleDebugMenu: Instance Not Found")); return false; } if(GDMManager->IsShowingDebugMenu()) { GDMManager->HideDebugMenu(); } else { GDMManager->ShowDebugMenu(); } return true; } bool UGameDebugMenuFunctions::IsShowingDebugMenu(UObject* WorldContextObject) { AGameDebugMenuManager* GDMManager = GetGameDebugMenuManager(WorldContextObject); if(!IsValid(GDMManager)) { return false; } return GDMManager->IsShowingDebugMenu(); } UGameDebugMenuRootWidget* UGameDebugMenuFunctions::GetGameDebugMenuRootWidget(const UObject* WorldContextObject) { AGameDebugMenuManager* GDMManager = GetGameDebugMenuManager(WorldContextObject); if(!IsValid(GDMManager)) { return nullptr; } return GDMManager->GetDebugMenuRootWidget(); } bool UGameDebugMenuFunctions::RegisterGDMObjectProperty(UObject* TargetObject, const FGDMPropertyUIConfigInfo PropertyUIConfigInfo, const FName PropertyName, const FGDMGameplayCategoryKey CategoryKey, const FString PropertySaveKey, const FText DisplayPropertyName,const FText Description, const int32 DisplayPriority) { if(bDisableGameDebugMenu) { UE_LOG(LogGDM, Warning, TEXT("RegisterGDMObjectProperty: GameDebugMenu is disabled")); return false; } AGameDebugMenuManager* GDMManager = GetGameDebugMenuManager(TargetObject); if(!IsValid(GDMManager)) { /* まだ生成してないので一時キャッシュ */ FGDMPendingObjectData PendingData; PendingData.TargetObject = TargetObject; PendingData.TargetName = PropertyName; PendingData.CategoryKey = CategoryKey; PendingData.SaveKey = PropertySaveKey; PendingData.DisplayPropertyName = DisplayPropertyName; PendingData.Description = Description; PendingData.ConfigInfo = PropertyUIConfigInfo; PendingData.DisplayPriority = DisplayPriority; RegisterPendingProperties.Add(PendingData); return false; } return GDMManager->RegisterObjectProperty(TargetObject, PropertyName, CategoryKey, PropertySaveKey, DisplayPropertyName, Description, PropertyUIConfigInfo, DisplayPriority); } bool UGameDebugMenuFunctions::RegisterGDMObjectFunction(UObject* TargetObject, FName FunctionName, const FGDMGameplayCategoryKey CategoryKey, const FString FunctionSaveKey, const FText DisplayFunctionName,const FText Description, const int32 DisplayPriority) { if(bDisableGameDebugMenu) { UE_LOG(LogGDM, Warning, TEXT("RegisterGDMObjectFunction: GameDebugMenu is disabled")); return false; } AGameDebugMenuManager* GDMManager = GetGameDebugMenuManager(TargetObject); if(!IsValid(GDMManager)) { /* まだ生成してないので一時キャッシュ */ FGDMPendingObjectData PendingData; PendingData.TargetObject = TargetObject; PendingData.TargetName = FunctionName; PendingData.CategoryKey = CategoryKey; PendingData.SaveKey = FunctionSaveKey; PendingData.DisplayPropertyName = DisplayFunctionName; PendingData.Description = Description; PendingData.DisplayPriority = DisplayPriority; RegisterPendingFunctions.Add(PendingData); return false; } return GDMManager->RegisterObjectFunction(TargetObject, FunctionName, CategoryKey, FunctionSaveKey, DisplayFunctionName, Description, DisplayPriority); } void UGameDebugMenuFunctions::UnregisterGDMObject(UObject* TargetObject) { AGameDebugMenuManager* GDMManager = GetGameDebugMenuManager(TargetObject); if(!IsValid(GDMManager)) { return; } FGDMGameplayCategoryKey CategoryKey; FString PropertySaveKey; FText DisplayPropertyName; FText Description; FName PropertyName; EGDMPropertyType PropertyType; FString EnumPathName; FGDMPropertyUIConfigInfo PropertyUIConfigInfo; for(int32 Index = GDMManager->GetNumObjectProperties() - 1; Index >= 0; --Index) { UObject* RegisterObject = GDMManager->GetObjectProperty(Index, CategoryKey, PropertySaveKey, DisplayPropertyName, Description, PropertyName, PropertyType, EnumPathName, PropertyUIConfigInfo); if(!IsValid(RegisterObject) || (TargetObject == RegisterObject)) { GDMManager->RemoveObjectProperty(Index); } } FText DisplayFunctionName; FName FunctionName; for(int32 Index = GDMManager->GetNumObjectFunctions() - 1; Index >= 0; --Index) { UObject* RegisterObject = GDMManager->GetObjectFunction(Index, CategoryKey, PropertySaveKey, DisplayFunctionName, Description, FunctionName); if(!IsValid(RegisterObject) || (TargetObject == RegisterObject)) { GDMManager->RemoveObjectFunction(Index); } } } UObject* UGameDebugMenuFunctions::GetGDMObjectProperty(UObject* WorldContextObject,const int32 Index, FGDMGameplayCategoryKey& OutCategoryKey, FString& OutPropertySaveKey, FText& OutDisplayPropertyName, FText& OutDescription, FName& OutPropertyName, EGDMPropertyType& OutPropertyType, FString& OutEnumPathName, FGDMPropertyUIConfigInfo& PropertyUIConfigInfo) { AGameDebugMenuManager* GDMManager = GetGameDebugMenuManager(WorldContextObject); if(!IsValid(GDMManager)) { return nullptr; } return GDMManager->GetObjectProperty(Index, OutCategoryKey, OutPropertySaveKey, OutDisplayPropertyName, OutDescription, OutPropertyName, OutPropertyType, OutEnumPathName, PropertyUIConfigInfo); } UObject* UGameDebugMenuFunctions::GetGDMObjectFunction(UObject* WorldContextObject,const int32 Index, FGDMGameplayCategoryKey& OutCategoryKey, FString& OutFunctionSaveKey, FText& OutDisplayFunctionName, FText& OutDescription, FName& OutFunctionName) { AGameDebugMenuManager* GDMManager = GetGameDebugMenuManager(WorldContextObject); if(!IsValid(GDMManager)) { return nullptr; } return GDMManager->GetObjectFunction(Index, OutCategoryKey, OutFunctionSaveKey, OutDisplayFunctionName, OutDescription, OutFunctionName); } int32 UGameDebugMenuFunctions::GetGDMNumObjectProperties(UObject* WorldContextObject) { AGameDebugMenuManager* GDMManager = GetGameDebugMenuManager(WorldContextObject); if(!IsValid(GDMManager)) { return 0; } return GDMManager->GetNumObjectProperties(); } int32 UGameDebugMenuFunctions::GetGDMNumObjectFunctions(UObject* WorldContextObject) { AGameDebugMenuManager* GDMManager = GetGameDebugMenuManager(WorldContextObject); if(!IsValid(GDMManager)) { return 0; } return GDMManager->GetNumObjectFunctions(); } bool UGameDebugMenuFunctions::VerifyGDMNumObjectProperties(UObject* WorldContextObject) { UE_LOG(LogGDM,Verbose, TEXT("VerifyGDMNumObjectProperties")); AGameDebugMenuManager* GDMManager = GetGameDebugMenuManager(WorldContextObject); if(!IsValid(GDMManager)) { return false; } FGDMGameplayCategoryKey CategoryKey; FString PropertySaveKey; FText DisplayPropertyName; FText Description; FName PropertyName; EGDMPropertyType PropertyType; FString EnumPathName; FGDMPropertyUIConfigInfo PropertyUIConfigInfo; int32 RemoveCount = 0; for(int32 Index = GetGDMNumObjectProperties(WorldContextObject) - 1; Index >= 0; --Index) { const UObject* PropertyOwnerObj = GetGDMObjectProperty(WorldContextObject, Index, CategoryKey, PropertySaveKey, DisplayPropertyName, Description, PropertyName, PropertyType, EnumPathName, PropertyUIConfigInfo); if(!IsValid(PropertyOwnerObj)) { GDMManager->RemoveObjectProperty(Index); RemoveCount++; } } UE_LOG(LogGDM,Verbose, TEXT("VerifyGDMNumObjectProperties: Remove %d"), RemoveCount); return (RemoveCount != 0); } bool UGameDebugMenuFunctions::VerifyGDMNumObjectFunctions(UObject* WorldContextObject) { UE_LOG(LogGDM,Verbose, TEXT("VerifyGDMNumObjectFunctions")); AGameDebugMenuManager* GDMManager = GetGameDebugMenuManager(WorldContextObject); if(!IsValid(GDMManager)) { return false; } const int32 Num = GetGDMNumObjectFunctions(WorldContextObject); FGDMGameplayCategoryKey CategoryKey; FString FunctionSaveKey; FText DisplayFunctionName; FText Description; FName FunctionName; int32 RemoveCount = 0; for(int32 Index = Num - 1; Index >= 0; --Index) { const UObject* FunctionOwnerObj = GetGDMObjectFunction(WorldContextObject,Index, CategoryKey,FunctionSaveKey, DisplayFunctionName,Description,FunctionName); if(!IsValid(FunctionOwnerObj)) { GDMManager->RemoveObjectFunction(Index); RemoveCount++; } } UE_LOG(LogGDM,Verbose, TEXT("VerifyGDMNumObjectFunctions: Remove %d"), RemoveCount); return (RemoveCount == 0); } TArray UGameDebugMenuFunctions::GetGDMOrderGameplayCategoryTitle(UObject* WorldContextObject) { TArray ReturnValues; const auto& TitleArray = GetDefault()->OrderGameplayCategoryTitles; for( int32 Index = 0; Index < TitleArray.Num(); ++Index ) { ReturnValues.Add(FGDMMenuCategoryKey(TitleArray[Index].Index, TitleArray[Index].Title)); } return ReturnValues; } int32 UGameDebugMenuFunctions::GetGDMInvalidIndex() { return INDEX_NONE; } FString UGameDebugMenuFunctions::GetGDMObjectName(UObject* TargetObject) { if(!IsValid(TargetObject)) { return FString(); } #if WITH_EDITOR return UKismetSystemLibrary::GetDisplayName(TargetObject); #else return UKismetSystemLibrary::GetObjectName(TargetObject); #endif } void UGameDebugMenuFunctions::RegisterInputComponentForGameDebugMenu(UObject* WorldContextObject, UInputComponent* InputComponent) { if (AGameDebugMenuManager* GDMManager = GetGameDebugMenuManager(WorldContextObject)) { GDMManager->GetDebugMenuInputSystemComponent()->RegisterInputComponent(InputComponent); } } void UGameDebugMenuFunctions::UnregisterInputComponentForGameDebugMenu(UObject* WorldContextObject, UInputComponent* InputComponent) { if (AGameDebugMenuManager* GDMManager = GetGameDebugMenuManager(WorldContextObject)) { GDMManager->GetDebugMenuInputSystemComponent()->UnregisterInputComponent(InputComponent); } } bool UGameDebugMenuFunctions::ShowDebugReport(UObject* WorldContextObject) { AGameDebugMenuManager* GDMManager = GetGameDebugMenuManager(WorldContextObject); if(!IsValid(GDMManager)) { UE_LOG(LogGDM, Log, TEXT("RequestDebugReport: Instance not found")); return false; } if(GDMManager->ShowDebugMenu(true)) { return true; } return false; } EGDMProjectManagementTool UGameDebugMenuFunctions::GetGDMSelectedProjectManagementTool() { return GetDefault()->ProjectManagementToolType; } FString UGameDebugMenuFunctions::GetGDMBuildConfigurationString() { #if UE_EDITOR return TEXT("Editor"); #else return LexToString(FApp::GetBuildConfiguration()); #endif } FString UGameDebugMenuFunctions::GetGDMBuildVersionString() { return FApp::GetBuildVersion(); } FString UGameDebugMenuFunctions::GetGDMProjectVersionString() { const UGeneralProjectSettings& ProjectSettings = *GetDefault(); return *ProjectSettings.ProjectVersion; } TArray UGameDebugMenuFunctions::GetGDMCultureList() { return GetDefault()->CultureList; } void UGameDebugMenuFunctions::PrintLogScreen(UObject* WorldContextObject, const FString& InString, float Duration, bool bPrintToLog) { #if !(UE_BUILD_SHIPPING) static FLinearColor LogColor(0.15f, 0.2f, 0.2f); const UWorld* World = GEngine->GetWorldFromContextObject(WorldContextObject, EGetWorldErrorMode::ReturnNull); FString Prefix; if (World) { if (World->WorldType == EWorldType::PIE) { switch (World->GetNetMode()) { case NM_Client: { /* UE::GetPlayInEditorID() はマルチPIEで "ambiguous PIE world" 警告を出すことがあるので Worldに紐づくPIE Instance Idを使用する*/ const UPackage* Pkg = World->GetPackage(); const int32 PieInstanceId = Pkg ? Pkg->GetPIEInstanceID() : INDEX_NONE; Prefix = FString::Printf(TEXT("Client PIE_%d: "), PieInstanceId); } break; case NM_DedicatedServer: case NM_ListenServer: Prefix = FString::Printf(TEXT("Server: ")); break; case NM_Standalone: break; default: ; } } } const FString FinalDisplayString = Prefix + InString; FString FinalLogString = FinalDisplayString; static const FBoolConfigValueHelper DisplayPrintStringSource(TEXT("Kismet"), TEXT("bLogPrintStringSource"), GEngineIni); if (DisplayPrintStringSource) { const FString SourceObjectPrefix = FString::Printf(TEXT("[%s] "), *GetNameSafe(WorldContextObject)); FinalLogString = SourceObjectPrefix + FinalLogString; } if (GAreScreenMessagesEnabled) { if (GConfig && Duration < 0) { GConfig->GetFloat(TEXT("Kismet"), TEXT("PrintStringDuration"), Duration, GEngineIni); } GEngine->AddOnScreenDebugMessage((uint64)-1, Duration, LogColor.ToFColor(true), TEXT("GDM: ") + FinalDisplayString); } else { UE_LOG(LogGDM, Log, TEXT("Screen messages disabled (!GAreScreenMessagesEnabled). Cannot print to screen.")); } if( bPrintToLog ) { UE_LOG(LogGDM, Log, TEXT("%s"), *FinalDisplayString); } else { UE_LOG(LogGDM, Verbose, TEXT("%s"), *FinalLogString); } #endif } bool UGameDebugMenuFunctions::GetDebugMenuString(UObject* WorldContextObject, const FString StringKey, FString& OutString) { if(const AGameDebugMenuManager* Manager = GetGameDebugMenuManager(WorldContextObject) ) { return Manager->GetLocalizeStringComponent()->GetString(StringKey, OutString); } OutString = StringKey; return false; } FName UGameDebugMenuFunctions::GetCurrentDebugMenuLanguage(UObject* WorldContextObject) { if(const AGameDebugMenuManager* Manager = GetGameDebugMenuManager(WorldContextObject) ) { return Manager->GetLocalizeStringComponent()->GetCurrentDebugMenuLanguage(); } return NAME_None; } TArray UGameDebugMenuFunctions::GetDebugMenuLanguageKeys() { return GetDefault()->GetDebugMenuLanguageKeys(); } FString UGameDebugMenuFunctions::GetDebugMenuLineBreakString() { return GetDefault()->LineBreakString; } FString UGameDebugMenuFunctions::BuildConsoleCommandId_FromSingle(const FGDMConsoleCommandSingle& Command) { return Command.BuildCommandIdentifier(); } FString UGameDebugMenuFunctions::BuildConsoleCommandId_FromGroup(const FGDMConsoleCommandGroup& Command) { return Command.BuildCommandIdentifier(); } FString UGameDebugMenuFunctions::BuildConsoleCommandId_FromPair(const FGDMConsoleCommandPair& Command) { return Command.BuildCommandIdentifier(); } FString UGameDebugMenuFunctions::BuildConsoleCommandId_FromNumber(const FGDMConsoleCommandNumber& Command) { return Command.BuildCommandIdentifier(); } bool UGameDebugMenuFunctions::EqualEqual_GDMMenuCategoryKey(const FGDMMenuCategoryKey& A, const FGDMMenuCategoryKey& B) { return A.Index == B.Index; } bool UGameDebugMenuFunctions::NotEqual_GDMMenuCategoryKey(const FGDMMenuCategoryKey& A, const FGDMMenuCategoryKey& B) { return A.Index != B.Index; } uint8 UGameDebugMenuFunctions::Conv_GDMMenuCategoryKeyToByte(const FGDMMenuCategoryKey& Key) { return Key.Index; } FGDMMenuCategoryKey UGameDebugMenuFunctions::Conv_ByteToGDMMenuCategoryKey(const uint8& Index) { return FGDMMenuCategoryKey(Index); } void UGameDebugMenuFunctions::OnActorSpawnedClientWaitManager(AGameDebugMenuManager* SpawnDebugMenuManager) { /* マネージャー生成前に登録処理したプロパティ群を追加する */ for(const auto& PendingData : RegisterPendingProperties) { if(PendingData.TargetObject.IsValid()) { if( PendingData.TargetObject->GetWorld() == SpawnDebugMenuManager->GetWorld() ) { RegisterGDMObjectProperty(PendingData.TargetObject.Get() ,PendingData.ConfigInfo ,PendingData.TargetName ,PendingData.CategoryKey ,PendingData.SaveKey ,PendingData.DisplayPropertyName ,PendingData.Description ,PendingData.DisplayPriority ); } } } /* マネージャー生成前に登録処理した関数群を追加する */ for(const auto& PendingData : RegisterPendingFunctions) { if(PendingData.TargetObject.IsValid()) { if( PendingData.TargetObject->GetWorld() == SpawnDebugMenuManager->GetWorld() ) { RegisterGDMObjectFunction(PendingData.TargetObject.Get() ,PendingData.TargetName ,PendingData.CategoryKey ,PendingData.SaveKey ,PendingData.DisplayPropertyName ,PendingData.Description ,PendingData.DisplayPriority ); } } } RegisterPendingProperties.RemoveAll([&](const FGDMPendingObjectData& PropertyData) { if (!PropertyData.TargetObject.IsValid()) { return true; } return (PropertyData.TargetObject->GetWorld() == SpawnDebugMenuManager->GetWorld()); }); RegisterPendingFunctions.RemoveAll([&](const FGDMPendingObjectData& FunctionData) { if (!FunctionData.TargetObject.IsValid()) { return true; } return (FunctionData.TargetObject->GetWorld() == SpawnDebugMenuManager->GetWorld()); }); UE_LOG(LogGDM, Log, TEXT("OnActorSpawnedClientWaitManager: Spawn GameDebugMenuManager")); } void UGameDebugMenuFunctions::OnActorSpawnedServer(AActor* SpawnActor) { if (!IsValid(SpawnActor)) { return; } if (!SpawnActor->IsA(APlayerController::StaticClass())) { return; } AGameDebugMenuManager* DebugMenuManager = GetGameDebugMenuManager(SpawnActor); if (!IsValid(DebugMenuManager)) { UE_LOG(LogGDM, Warning, TEXT("OnActorSpawnedServer: Not found DebugMenuManager")); return; } DebugMenuManager->AddDebugMenuPCProxyComponent(Cast(SpawnActor)); } void UGameDebugMenuFunctions::ShowDebugConsoleCommand() { for (const auto Manager : GGameDebugMenuManagers ) { if (IsValid(Manager->GetWorld()) && IsValid(Manager->GetOwner())) { Manager->ShowDebugMenu(); break; } } } void UGameDebugMenuFunctions::HideDebugConsoleCommand() { for (const auto Manager : GGameDebugMenuManagers ) { if (IsValid(Manager->GetWorld()) && IsValid(Manager->GetOwner())) { Manager->HideDebugMenu(); break; } } } void UGameDebugMenuFunctions::ToggleDebugConsoleCommand() { for (const auto Manager : GGameDebugMenuManagers ) { if (IsValid(Manager->GetWorld()) && IsValid(Manager->GetOwner())) { if( Manager->IsShowingDebugMenu()) { Manager->HideDebugMenu(); } else { Manager->ShowDebugMenu(); } break; } } } void UGameDebugMenuFunctions::ToggleInputSystemLog() { for (const auto Manager : GGameDebugMenuManagers ) { if (IsValid(Manager->GetWorld()) && IsValid(Manager->GetOwner())) { UGDMInputSystemComponent* InputSystemComponent = Manager->GetDebugMenuInputSystemComponent(); InputSystemComponent->bOutputDebugLog = !InputSystemComponent->bOutputDebugLog; break; } } } ``` `SampleProject/Plugins/GameDebugMenu/Source/GameDebugMenu/Private/GameDebugMenuManager.cpp`: ```cpp /** * Copyright (c) 2020 akihiko moroi * * This software is released under the MIT License. * (See accompanying file LICENSE.txt or copy at http://opensource.org/licenses/MIT) */ #include "GameDebugMenuManager.h" #include "Engine/DebugCameraController.h" #include "GameFramework/CheatManager.h" #include "GameFramework/WorldSettings.h" #include "Kismet/KismetStringLibrary.h" #include "Kismet/KismetSystemLibrary.h" #include "Kismet/GameplayStatics.h" #include "TimerManager.h" #include "Blueprint/GameViewportSubsystem.h" #include "Blueprint/WidgetBlueprintLibrary.h" #include "Framework/Application/SlateApplication.h" #include "GameDebugMenuSettings.h" #include "GameDebugMenuFunctions.h" #include "Log/GDMOutputDevice.h" #include "Component/GDMListenerComponent.h" #include "Component/GDMPlayerControllerProxyComponent.h" #include "Component/GDMScreenshotRequesterComponent.h" #include "Component/GDMLocalizeStringComponent.h" #include "Component/GDMPropertyJsonSystemComponent.h" #include "Component/GDMSaveSystemComponent.h" #include "ConsoleCommand/GDMConsoleCommandValueProviderComponent.h" #include "Input/GDMInputSystemComponent.h" #include "Widgets/GameDebugMenuRootWidget.h" #include "Widgets/GDMTextBlock.h" #include "Data/GameDebugMenuManagerAsset.h" #include "Favorite/GDMFavoriteSystemComponent.h" /********************************************************************/ /* AGameDebugMenuManager */ /********************************************************************/ AGameDebugMenuManager::AGameDebugMenuManager(const FObjectInitializer& ObjectInitializer) : Super(ObjectInitializer) , DebugMenuInputSystemComponent(nullptr) , ScreenshotRequesterComponent(nullptr) , PropertyJsonSystemComponent(nullptr) , SaveSystemComponent(nullptr) , LocalizeStringComponent(nullptr) , ListenerComponent(nullptr) , InitializeManagerHandle() , bInitializedManager(false) , MenuAsset(nullptr) , ConsoleCommandSetAsset(nullptr) , bShowDebugMenu(false) , bCachedGamePaused(false) , bCachedShowMouseCursor(false) , bWaitToCaptureBeforeOpeningDebugReportMenu(false) , CachedNavigationConfigs() , ObjectProperties() , ObjectFunctions() , DebugMenuRootWidget(nullptr) , DebugMenuInstances() , OutputLog(nullptr) { DebugMenuInputSystemComponent = CreateDefaultSubobject(TEXT("DebugMenuInputSystemComponent")); ScreenshotRequesterComponent = CreateDefaultSubobject(TEXT("ScreenshotRequesterComponent")); PropertyJsonSystemComponent = CreateDefaultSubobject(TEXT("PropertyJsonSystemComponent")); SaveSystemComponent = CreateDefaultSubobject(TEXT("SaveSystemComponent")); FavoriteSystemComponent = CreateDefaultSubobject(TEXT("FavoriteSystemComponent")); ConsoleCommandValueProviderComponent = CreateDefaultSubobject(TEXT("ConsoleCommandValueProviderComponent"));; LocalizeStringComponent = CreateDefaultSubobject(TEXT("LocalizeStringComponent")); ListenerComponent = CreateDefaultSubobject(TEXT("ListenerComponent")); PrimaryActorTick.bCanEverTick = true; PrimaryActorTick.bStartWithTickEnabled = true; PrimaryActorTick.bTickEvenWhenPaused = true; SetCanBeDamaged(false); SetHidden(true); InputPriority = TNumericLimits::Max(); bBlockInput = false; SetRemoteRoleForBackwardsCompat(ROLE_SimulatedProxy); bReplicates = true; bAlwaysRelevant = false; SetReplicatingMovement(false); SetNetUpdateFrequency(1.0f);/* デフォルトのPlayerStateと同じかんじにしとく */ bOnlyRelevantToOwner = true; } void AGameDebugMenuManager::BeginPlay() { Super::BeginPlay(); /* DedicatedServer ではローカルUIが無いので何もしない */ if (UKismetSystemLibrary::IsDedicatedServer(this)) { return; } /* BeginPlay時点でOwnerが未確定なケースがある(レプリケーション生成など) * Ownerが確定して「ローカルPCのManager」であることが分かったときだけ初期化する。*/ auto TryLocalInit = [this]() { if (bLocalBeginPlayInitialized) { return; } const APlayerController* PC = Cast(GetOwner()); if (!IsValid(PC)) { return; /* まだOwner未確定、後で再試行 */ } if (!PC->IsLocalController()) { /* Ownerが確定していてローカルでないなら、このManagerはこのプロセスでは初期化しない */ bLocalBeginPlayInitialized = true; return; } bLocalBeginPlayInitialized = true; OutputLog = MakeShared(); /* 他で参照される前にロードは処理しとく */ GetSaveSystemComponent()->LoadDebugMenuFile(); UGameDebugMenuFunctions::RegisterGameDebugMenuManagerInstance(this); UGameDebugMenuFunctions::PrintLogScreen(this, TEXT("AGameDebugMenuManager: Call BeginPlay"), 4.0f); /* managerのBeginplayがちゃんと完了後に処理↓ */ GetWorld()->GetTimerManager().SetTimer(InitializeManagerHandle, FTimerDelegate::CreateLambda([this]() { OnInitializeManager(); }), 0.01f, true); }; TryLocalInit(); /* Owner未確定なら短時間だけリトライ(Ownerが確定したら止まる) */ if (!bLocalBeginPlayInitialized) { GetWorld()->GetTimerManager().SetTimer(LocalBeginPlayRetryHandle, FTimerDelegate::CreateLambda([this, TryLocalInit]() { TryLocalInit(); if (bLocalBeginPlayInitialized) { GetWorld()->GetTimerManager().ClearTimer(LocalBeginPlayRetryHandle); } }), 0.1f, true); } } void AGameDebugMenuManager::EndPlay(const EEndPlayReason::Type EndPlayReason) { UGameDebugMenuFunctions::PrintLogScreen(this, TEXT("AGameDebugMenuManager: Call EndPlay"), 4.0f); OutputLog.Reset(); if(EndPlayReason != EEndPlayReason::EndPlayInEditor && EndPlayReason != EEndPlayReason::Quit) { if (const UWorld* World = GetWorld()) { World->GetTimerManager().ClearTimer(InitializeManagerHandle); } EnabledNavigationConfigs(); if(IsValid(DebugMenuRootWidget)) { DebugMenuRootWidget->RemoveFromParent(); DebugMenuRootWidget = nullptr; } } Super::EndPlay(EndPlayReason); UGameDebugMenuFunctions::UnregisterGameDebugMenuManagerInstance(this); } void AGameDebugMenuManager::Tick(float DeltaTime) { /* Slomoなど時間操作に影響受けないようにする */ DeltaTime /= GetWorldSettings()->GetEffectiveTimeDilation(); Super::Tick(DeltaTime); } UGDMInputSystemComponent* AGameDebugMenuManager::GetDebugMenuInputSystemComponent() const { return DebugMenuInputSystemComponent; } UGDMScreenshotRequesterComponent* AGameDebugMenuManager::GetScreenshotRequesterComponent() const { return ScreenshotRequesterComponent; } UGDMPropertyJsonSystemComponent* AGameDebugMenuManager::GetPropertyJsonSystemComponent() const { return PropertyJsonSystemComponent; } UGDMSaveSystemComponent* AGameDebugMenuManager::GetSaveSystemComponent() const { return SaveSystemComponent; } UGDMFavoriteSystemComponent* AGameDebugMenuManager::GetFavoriteSystemComponent() const { return FavoriteSystemComponent; } UGDMConsoleCommandValueProviderComponent* AGameDebugMenuManager::GetConsoleCommandValueProviderComponent() const { return ConsoleCommandValueProviderComponent; } UGDMLocalizeStringComponent* AGameDebugMenuManager::GetLocalizeStringComponent() const { return LocalizeStringComponent; } UGDMListenerComponent* AGameDebugMenuManager::GetListenerComponent() const { return ListenerComponent; } bool AGameDebugMenuManager::IsInitializedManager() const { return bInitializedManager; } void AGameDebugMenuManager::OnInitializeManager() { if (bInitializedManager) { UGameDebugMenuFunctions::PrintLogScreen(this, TEXT("AGameDebugMenuManager: The manager has already been initialized"), 0.6f); return; } if (GetOwner() == nullptr) { UGameDebugMenuFunctions::PrintLogScreen(this, TEXT("AGameDebugMenuManager: Not found Owner"), 0.6f); return; } APlayerController* PC = Cast(GetOwner()); if (!IsValid(PC)) { UGameDebugMenuFunctions::PrintLogScreen(this, TEXT("AGameDebugMenuManager: The owner is not a PlayerController"), 0.6f); return; } if (!IsValid(PC->GetLocalPlayer())) { if (PC->IsLocalController()) { UGameDebugMenuFunctions::PrintLogScreen(this, TEXT("AGameDebugMenuManager: No LocalPlayer"), 0.6f); return; } } /* BP側の準備完了を待つ(例:必要なアセットロード待ちなど) */ if (!IsReadyToInitializeManager()) { return; } bInitializedManager = true; GetLocalizeStringComponent()->SetJsonSystemComponentValue(GetPropertyJsonSystemComponent()); GetLocalizeStringComponent()->SyncLoadDebugMenuStringTables(); if( !UKismetSystemLibrary::IsDedicatedServer(this) && PC->IsLocalController() ) { GetFavoriteSystemComponent()->Initialize(MenuAsset); CreateDebugMenuRootWidget(); GetDebugMenuInputSystemComponent()->Initialize(MenuAsset); TArray FoundWidgets; UWidgetBlueprintLibrary::GetAllWidgetsOfClass(this, FoundWidgets, UGameDebugMenuWidget::StaticClass(), true); UGameViewportSubsystem* GameViewportSubsystem = UGameViewportSubsystem::Get(); GameViewportSubsystem->OnWidgetAdded.AddUObject(this, &AGameDebugMenuManager::OnWidgetAdded); GameViewportSubsystem->OnWidgetRemoved.AddUObject(this, &AGameDebugMenuManager::OnWidgetRemoved); const ULocalPlayer* OwnerLocalPlayer = PC->GetLocalPlayer(); for(const auto W : FoundWidgets) { if(UGameDebugMenuWidget* DebugMenuWidget = Cast(W)) { /* マルチPIE(複数ウィンドウ/複数World)では、全Widget列挙が他World/他LocalPlayerを混ぜることがある。 このManagerのOwner LocalPlayerに紐づくWidgetだけを対象にする*/ if (!IsValid(OwnerLocalPlayer) || DebugMenuWidget->GetOwningLocalPlayer() == OwnerLocalPlayer) { ViewportDebugMenuWidgets.AddUnique(DebugMenuWidget); } } } } if (!IsValid(PC->CheatManager)) { if (GetNetMode() == NM_Client) { /* クライアントでも利用できるように常に有効化 */ PC->EnableCheats(); } } TArray CommandHistory = GetPropertyJsonSystemComponent()->GetCustomStringArray(TEXT("CommandHistory")); for(const auto& Command : CommandHistory ) { PC->ConsoleCommand(Command); } GetWorld()->GetTimerManager().ClearTimer(InitializeManagerHandle); EnableInput(PC); OnInitializeManagerBP(); } void AGameDebugMenuManager::CreateDebugMenuRootWidget() { if( !IsValid(MenuAsset) ) { UE_LOG(LogGDM, Warning, TEXT("CreateDebugMenuRootWidget: Not found MenuAsset")); return; } if( IsValid(DebugMenuRootWidget) ) { DebugMenuRootWidget->RemoveFromParent(); } DebugMenuRootWidget = Cast(UWidgetBlueprintLibrary::Create(this, MenuAsset->DebugMenuRootWidgetClass, GetOwnerPlayerController())); DebugMenuRootWidget->SetDebugMenuManager(this); DebugMenuRootWidget->AddToViewport(MenuAsset->RootWidgetZOrder); DebugMenuRootWidget->SetVisibility(ESlateVisibility::Collapsed); DebugMenuRootWidget->EnsureDebugMenuInputComponent(); DebugMenuRootWidget->InitializeRootWidget(); } void AGameDebugMenuManager::EnabledNavigationConfigs() { if( CachedNavigationConfigs.Num() > 0 ) { /* Navigationを戻す */ FSlateApplication::Get().SetNavigationConfig(CachedNavigationConfigs[0]); CachedNavigationConfigs.Empty(); } } void AGameDebugMenuManager::DisabledNavigationConfigs() { /* Navigationを切る */ CachedNavigationConfigs.Empty(); CachedNavigationConfigs.Add(FSlateApplication::Get().GetNavigationConfig()); FSlateApplication::Get().SetNavigationConfig(MakeShared()); } void AGameDebugMenuManager::EnableShowMouseCursorFlag(APlayerController* PlayerController) { bCachedShowMouseCursor = PlayerController->bShowMouseCursor; PlayerController->bShowMouseCursor = true; if(!IsValid(PlayerController->CheatManager)) { return; } if(!IsValid(PlayerController->CheatManager->DebugCameraControllerRef)) { return; } /* DebugCamera操作中Menuを出すとマウスが表示されないのでこっちも合わせて更新 */ PlayerController->CheatManager->DebugCameraControllerRef->bShowMouseCursor = true; } void AGameDebugMenuManager::RestoreShowMouseCursorFlag(APlayerController* PlayerController) const { PlayerController->bShowMouseCursor = bCachedShowMouseCursor; if(!IsValid(PlayerController->CheatManager)) { return; } if(!IsValid(PlayerController->CheatManager->DebugCameraControllerRef)) { return; } /* DebugCameraも合わせて更新 */ PlayerController->CheatManager->DebugCameraControllerRef->bShowMouseCursor = bCachedShowMouseCursor; } void AGameDebugMenuManager::TryEnableGamePause() { check(IsValid(MenuAsset)); if(MenuAsset->bGamePause) { bCachedGamePaused = UGameplayStatics::IsGamePaused(this); UGameplayStatics::SetGamePaused(this, true); } } void AGameDebugMenuManager::RestoreGamePause() const { check(IsValid(MenuAsset)); if(MenuAsset->bGamePause) { UGameplayStatics::SetGamePaused(this, bCachedGamePaused); } } void AGameDebugMenuManager::OnScreenshotRequestProcessed() { bWaitToCaptureBeforeOpeningDebugReportMenu = false; GetListenerComponent()->OnScreenshotRequestProcessedDispatcher.RemoveDynamic(this, &AGameDebugMenuManager::OnScreenshotRequestProcessed); SetIgnoreInput(false); GetDebugMenuInputSystemComponent()->OnOpenMenu(); TArray DebugMenuWidgets = GetViewportDebugMenuWidgets(); for(const auto ViewportWidget : DebugMenuWidgets ) { if( ViewportWidget->IsActivateDebugMenu() ) { ViewportWidget->OnShowingMenu(/* bRequestDebugMenuManager */true); } } GetDebugMenuRootWidget()->ShowDebugReport(); CallShowDispatcher(); } void AGameDebugMenuManager::ExecuteConsoleCommand(const FString& Command, APlayerController* PC) { if(Command.IsEmpty()) { return; } const UWorld* World = GetWorld(); if(!IsValid(World)) { return; } if(!IsValid(PC)) { return; } FString LogCommand; if(IsValid(PC->CheatManager)) { if(ADebugCameraController* DCC = PC->CheatManager->DebugCameraControllerRef) { if(!DCC->IsInState(NAME_Inactive)) { /* DebugCameraControllerが動作してた場合はそっちで実行 */ LogCommand = DCC->ConsoleCommand(Command); UGameDebugMenuFunctions::PrintLogScreen(this, FString::Printf(TEXT("ExecuteConsoleCommand DCC: %s: Log %s"), *Command, *LogCommand), 4.0f); CallExecuteConsoleCommandDispatcher(Command); return; } } } LogCommand = PC->ConsoleCommand(Command); UGameDebugMenuFunctions::PrintLogScreen(this, FString::Printf(TEXT("ExecuteConsoleCommand PC: %s: Log %s"), *Command, *LogCommand), 4.0f); CallExecuteConsoleCommandDispatcher(Command); } bool AGameDebugMenuManager::ShowDebugMenu(bool bWaitToCaptureBeforeOpeningMenuFlag) { if(bShowDebugMenu) { UGameDebugMenuFunctions::PrintLogScreen(this, TEXT("ShowDebugMenu: bShowDebugMenu TRUE"), 4.0f); return false; } const UWorld* World = GetWorld(); if(!IsValid(World)) { UGameDebugMenuFunctions::PrintLogScreen(this, TEXT("ShowDebugMenu: Not found World"), 4.0f); return false; } if(!IsValid(DebugMenuRootWidget)) { UGameDebugMenuFunctions::PrintLogScreen(this, TEXT("ShowDebugMenu: Not found DebugMenuRootWidget"), 4.0f); return false; } bShowDebugMenu = true; APlayerController* PC = GetOwnerPlayerController(); DisabledNavigationConfigs(); EnableShowMouseCursorFlag(PC); TryEnableGamePause(); bool bShow = true; if( !GetDefault()->bDisableScreenCaptureProcessingWhenOpeningDebugMenu ) { GetScreenshotRequesterComponent()->RequestScreenshot(); if( bWaitToCaptureBeforeOpeningMenuFlag ) { /* 画面キャプチャ後UIを出す必要がある */ /* このフレームではメニューを開かない */ bShow = false; /* キャプチャ後メニューを開けるようにするための識別フラグをたてる */ bWaitToCaptureBeforeOpeningDebugReportMenu = true; /* 開くまで入力を無視するようにする */ SetIgnoreInput(true); GetListenerComponent()->OnScreenshotRequestProcessedDispatcher.AddDynamic(this, &AGameDebugMenuManager::OnScreenshotRequestProcessed); } } if( bShow ) { GetDebugMenuInputSystemComponent()->OnOpenMenu(); TArray DebugMenuWidgets = GetViewportDebugMenuWidgets(); for(const auto ViewportWidget : DebugMenuWidgets ) { if( ViewportWidget->IsActivateDebugMenu() ) { ViewportWidget->OnShowingMenu(/* bRequestDebugMenuManager */true); } } CallShowDispatcher(); UE_LOG(LogGDM, Log, TEXT("ShowDebugMenu")); } return true; } void AGameDebugMenuManager::HideDebugMenu() { if(!bShowDebugMenu) { UGameDebugMenuFunctions::PrintLogScreen(this, TEXT("HideDebugMenu: bShowDebugMenu FALSE"), 4.0f); return; } if(bWaitToCaptureBeforeOpeningDebugReportMenu) { /* キャプチャ後1度UIを開く予定なので閉じないようにする */ UGameDebugMenuFunctions::PrintLogScreen(this, TEXT("HideDebugMenu: bWaitToCaptureBeforeOpeningMenu TRUE"), 4.0f); return; } const UWorld* World = GetWorld(); if(!IsValid(World)) { UGameDebugMenuFunctions::PrintLogScreen(this, TEXT("HideDebugMenu: Not found World"), 4.0f); return; } if(!IsValid(DebugMenuRootWidget)) { UGameDebugMenuFunctions::PrintLogScreen(this, TEXT("HideDebugMenu: Not found DebugMenuWidget"), 4.0f); return; } GetDebugMenuInputSystemComponent()->OnCloseMenu(); bShowDebugMenu = false; APlayerController* PC = GetOwnerPlayerController(); EnabledNavigationConfigs(); RestoreShowMouseCursorFlag(PC); RestoreGamePause(); GetSaveSystemComponent()->SaveDebugMenuFile(); TArray DebugMenuWidgets = GetViewportDebugMenuWidgets(); for(const auto ViewportWidget : DebugMenuWidgets ) { if( ViewportWidget->IsActivateDebugMenu() ) { ViewportWidget->OnHidingMenu(/* bRequestDebugMenuManager */true); } } CallHideDispatcher(); UE_LOG(LogGDM, Log, TEXT("HideDebugMenu")); } bool AGameDebugMenuManager::IsShowingDebugMenu() { return bShowDebugMenu; } APlayerController* AGameDebugMenuManager::GetOwnerPlayerController() const { checkf(IsValid(GetOwner()), TEXT("No owner")); checkf(IsValid(Cast(GetOwner())), TEXT("Owner needs to be PlayerController")); return Cast(GetOwner()); } UGameDebugMenuRootWidget* AGameDebugMenuManager::GetDebugMenuRootWidget() { return DebugMenuRootWidget; } int32 AGameDebugMenuManager::GetAllDebugMenuKeys(TArray& OutKeys) { if( IsValid(MenuAsset) ) { OutKeys = MenuAsset->DebugMenuRegistrationOrder; } else { UE_LOG(LogGDM, Warning, TEXT("GetAllDebugMenuKeys: Not found MenuAsset")); } return OutKeys.Num(); } TSubclassOf AGameDebugMenuManager::GetDebugMenuWidgetClass(const FString& Key) { if( IsValid(MenuAsset) ) { if(const TSubclassOf* WidgetClass = MenuAsset->DebugMenuClasses.Find(Key) ) { return (*WidgetClass); } } else { UE_LOG(LogGDM, Warning, TEXT("GetAllDebugMenuKeys: Not found MenuAsset")); } return nullptr; } FString AGameDebugMenuManager::GetDebugMenuWidgetKey(const UGameDebugMenuWidget* Widget) { for (const auto& Pair : DebugMenuInstances) { if( Pair.Value == Widget ) { return Pair.Key; } } return TEXT(""); } bool AGameDebugMenuManager::GetDebugMenuWidgetInstances(TArray& OutInstances) { TArray> Array; DebugMenuInstances.GenerateValueArray(Array); OutInstances.Reset(); OutInstances.Reserve(Array.Num()); for (auto& A : Array) { OutInstances.Add(A); } return (OutInstances.Num() > 0); } void AGameDebugMenuManager::GetOutputLogString(FString& OutLog, const FString& Separator) { OutLog = UKismetStringLibrary::JoinStringArray(OutputLog->GetLogs(), Separator); } void AGameDebugMenuManager::GetOutputCommandHistoryString(TArray& OutCommandHistory) { OutCommandHistory = OutputLog->GetCommandHistory(); } void AGameDebugMenuManager::ClearCommandHistory() { OutputLog->ClearCommandHistory(); } EGDMPropertyType AGameDebugMenuManager::GetPropertyType(const FProperty* TargetProperty) const { if(TargetProperty != nullptr) { /* メニューで対応できるプロパティかチェック */ if(TargetProperty->IsA(FBoolProperty::StaticClass())) { return EGDMPropertyType::GDM_Bool; } else if(TargetProperty->IsA(FIntProperty::StaticClass())) { return EGDMPropertyType::GDM_Int; } else if(TargetProperty->IsA(FFloatProperty::StaticClass())) { return EGDMPropertyType::GDM_Float; } else if(TargetProperty->IsA(FDoubleProperty::StaticClass())) { return EGDMPropertyType::GDM_Float; } else if(TargetProperty->IsA(FEnumProperty::StaticClass())) { return EGDMPropertyType::GDM_Enum; } else if(TargetProperty->IsA(FByteProperty::StaticClass())) { return EGDMPropertyType::GDM_Byte; } else if(TargetProperty->IsA(FStrProperty::StaticClass())) { return EGDMPropertyType::GDM_String; } else if( TargetProperty->IsA(FStructProperty::StaticClass()) ) { const FStructProperty* StructProp = CastFieldChecked(TargetProperty); if( StructProp->Struct->GetFName() == NAME_Vector2D ) { return EGDMPropertyType::GDM_Vector2D; } else if( StructProp->Struct->GetFName() == NAME_Vector ) { return EGDMPropertyType::GDM_Vector; } else if( StructProp->Struct->GetFName() == NAME_Rotator ) { return EGDMPropertyType::GDM_Rotator; } } } return EGDMPropertyType::GDM_Null; } #define SET_VARIANT_PROPERTY(PropertyClass) \ { \ OldValueVariant = CastField(Property)->GetPropertyValue(ValuePtr); \ } #define SET_VARIANT_STRUCT_PROPERTY(Class) \ { \ const FStructProperty* StructProperty = CastField(Property); \ OldValueVariant = *StructProperty->ContainerPtrToValuePtr(TargetObject); \ } #define SET_VARIANT_ENUM_PROPERTY() \ { \ const FEnumProperty* EnumProperty = CastField(Property); \ const FNumericProperty* NumProp = EnumProp->GetUnderlyingProperty(); \ const uint64 Value = NumProp->GetUnsignedIntPropertyValue(EnumProp->ContainerPtrToValuePtr(TargetObject)); \ OldValueVariant = static_cast( Value ); \ } #define CALL_PROPERTY_DISPATCHER(Type, PropertyClass, ValueType) \ { \ const PropertyClass* TypedProperty = CastField(Property); \ ValueType CurrentValue = TypedProperty->GetPropertyValue(ValuePtr); \ ValueType OldValue = OldValueVariant.GetValue(); \ if (CurrentValue != OldValue) \ { \ CallChangeProperty##Type##Dispatcher(PropertyName, TargetObject, CurrentValue, OldValue, PropertySaveKey); \ } \ } #define CALL_STRUCT_PROPERTY_DISPATCHER(Type, StructType) \ { \ const FStructProperty* StructProperty = CastField(Property); \ StructType CurrentValue = *StructProperty->ContainerPtrToValuePtr(TargetObject); \ StructType OldValue = OldValueVariant.GetValue(); \ if (CurrentValue != OldValue) \ { \ CallChangeProperty##Type##Dispatcher(PropertyName, TargetObject, CurrentValue, OldValue, PropertySaveKey); \ } \ } #define CALL_ENUM_PROPERTY_DISPATCHER() \ { \ const FEnumProperty* EnumProperty = CastField(Property); \ const FNumericProperty* NumProp = EnumProperty->GetUnderlyingProperty(); \ uint8 CurrentValue = static_cast(NumProp->GetUnsignedIntPropertyValue(ValuePtr)); \ uint8 OldValue = OldValueVariant.GetValue(); \ if (CurrentValue != OldValue) \ { \ CallChangePropertyByteDispatcher(PropertyName, TargetObject, CurrentValue, OldValue, PropertySaveKey); \ } \ } bool AGameDebugMenuManager::RegisterObjectProperty(UObject* TargetObject, const FName PropertyName, const FGDMGameplayCategoryKey& CategoryKey, const FString& PropertySaveKey, const FText& DisplayPropertyName, const FText& Description, const FGDMPropertyUIConfigInfo& PropertyUIConfigInfo, const int32& DisplayPriority) { if(!IsValid(TargetObject)) { UE_LOG(LogGDM, Warning, TEXT("RegisterObjectProperty: Not found TargetObject")); return false; } FProperty* Property = TargetObject->GetClass()->FindPropertyByName(PropertyName); if( Property == nullptr ) { UE_LOG(LogGDM, Warning, TEXT("RegisterObjectProperty: Not found Property")); return false; } const EGDMPropertyType PropertyType = GetPropertyType(Property); if(PropertyType == EGDMPropertyType::GDM_Null) { UE_LOG(LogGDM, Warning, TEXT("RegisterObjectProperty: not supported Property: %s: %s"), *PropertyName.ToString(), *GetNameSafe(Property)); return false; } const TSharedPtr PropertyInfo = MakeShareable(new FGDMObjectPropertyInfo); PropertyInfo->CategoryKey = CategoryKey; PropertyInfo->Name = (DisplayPropertyName.IsEmpty() != false) ? FText::FromName(PropertyName) : DisplayPropertyName; PropertyInfo->TargetObject = TargetObject; PropertyInfo->PropertyName = PropertyName; PropertyInfo->TargetProperty = Property; PropertyInfo->ConfigInfo = PropertyUIConfigInfo; PropertyInfo->Description = Description; PropertyInfo->DisplayPriority = DisplayPriority; PropertyInfo->PropertySaveKey = PropertySaveKey; /* Enumならセット */ const FEnumProperty* EnumProp = CastField(Property);/* C++定義だとこっち */ const FByteProperty* ByteProp = CastField(Property);/* BP定義だとこっちみたい… */ const FStructProperty* StructProp = CastField(Property); if(EnumProp != nullptr) { PropertyInfo->EnumType = EnumProp->GetEnum(); } else if(ByteProp != nullptr) { PropertyInfo->EnumType = ByteProp->Enum; } else if( StructProp != nullptr ) { PropertyInfo->Struct = StructProp->Struct; } ObjectProperties.Add(PropertyInfo); ObjectProperties.Sort([](const TSharedPtr& A,const TSharedPtr& B) { return A->DisplayPriority >= B->DisplayPriority; }); if (!PropertySaveKey.IsEmpty()) { /* プロパティ型に応じた値を一時的に保存 */ FVariant OldValueVariant; void* ValuePtr = Property->ContainerPtrToValuePtr(TargetObject); switch (PropertyType) { case EGDMPropertyType::GDM_Bool: { SET_VARIANT_PROPERTY(FBoolProperty) break; } case EGDMPropertyType::GDM_Int: { SET_VARIANT_PROPERTY(FIntProperty) break; } case EGDMPropertyType::GDM_Float: { SET_VARIANT_PROPERTY(FFloatProperty) break; } case EGDMPropertyType::GDM_Enum: { SET_VARIANT_ENUM_PROPERTY() break; } case EGDMPropertyType::GDM_Byte: { SET_VARIANT_PROPERTY(FByteProperty) break; } case EGDMPropertyType::GDM_String: { SET_VARIANT_PROPERTY(FStrProperty) break; } case EGDMPropertyType::GDM_Vector: { SET_VARIANT_STRUCT_PROPERTY(FVector) break; } case EGDMPropertyType::GDM_Vector2D:{ SET_VARIANT_STRUCT_PROPERTY(FVector2D) break; } case EGDMPropertyType::GDM_Rotator: { SET_VARIANT_STRUCT_PROPERTY(FRotator) break; } default: UE_LOG(LogGDM, Warning, TEXT("Unsupported property type for old value caching")); } /* 保存キーを指定してるため、既に一致する情報があればそれをプロパティにセットを試みる */ if (!GetPropertyJsonSystemComponent()->ApplyJsonToObjectProperty(PropertySaveKey, TargetObject, PropertyName.ToString())) { /* 失敗、データがないので現状の値をJsonに書き込み */ GetPropertyJsonSystemComponent()->AddPropertyToJson(PropertySaveKey, TargetObject, PropertyName.ToString()); } else { switch (PropertyType) { case EGDMPropertyType::GDM_Bool: { CALL_PROPERTY_DISPATCHER(Bool, FBoolProperty, bool) break; } case EGDMPropertyType::GDM_Int: { CALL_PROPERTY_DISPATCHER(Int, FIntProperty, int32) break; } case EGDMPropertyType::GDM_Float: { CALL_PROPERTY_DISPATCHER(Float, FFloatProperty, float) break; } case EGDMPropertyType::GDM_Enum: { CALL_ENUM_PROPERTY_DISPATCHER() break; } case EGDMPropertyType::GDM_Byte: { CALL_PROPERTY_DISPATCHER(Byte, FByteProperty, uint8) break; } case EGDMPropertyType::GDM_String: { CALL_PROPERTY_DISPATCHER(String, FStrProperty, FString) break; } case EGDMPropertyType::GDM_Vector: { CALL_STRUCT_PROPERTY_DISPATCHER(Vector, FVector); break; } case EGDMPropertyType::GDM_Vector2D:{ CALL_STRUCT_PROPERTY_DISPATCHER(Vector2D, FVector2D); break; } case EGDMPropertyType::GDM_Rotator: { CALL_STRUCT_PROPERTY_DISPATCHER(Rotator, FRotator); break; } default: UE_LOG(LogGDM, Warning, TEXT("Unsupported property type for old value caching")); } } } return true; } bool AGameDebugMenuManager::RegisterObjectFunction(UObject* TargetObject, const FName FunctionName, const FGDMGameplayCategoryKey& CategoryKey, const FString& FunctionSaveKey, const FText& DisplayFunctionName,const FText& Description,const int32& DisplayPriority) { if(!IsValid(TargetObject)) { UE_LOG(LogGDM, Warning, TEXT("RegisterObjectFunction: Not found TargetObject")); return false; } UFunction* Function = TargetObject->GetClass()->FindFunctionByName(FunctionName); if( Function == nullptr ) { UE_LOG(LogGDM, Warning, TEXT("RegisterObjectFunction: Not found Function")); return false; } if (Function->NumParms != 0) { UE_LOG(LogGDM, Warning, TEXT("RegisterObjectFunction: Arguments are not supported: %d"), Function->NumParms); return false; } const TSharedPtr FunctionInfo = MakeShareable(new FGDMObjectFunctionInfo); FunctionInfo->CategoryKey = CategoryKey; FunctionInfo->Name = (DisplayFunctionName.IsEmpty() != false) ? FText::FromName(FunctionName) : DisplayFunctionName; FunctionInfo->TargetObject = TargetObject; FunctionInfo->FunctionName = FunctionName; FunctionInfo->TargetFunction = Function; FunctionInfo->Description = Description; FunctionInfo->DisplayPriority = DisplayPriority; FunctionInfo->FunctionSaveKey = FunctionSaveKey; ObjectFunctions.Add(FunctionInfo); ObjectFunctions.Sort([](const TSharedPtr& A,const TSharedPtr& B) { return A->DisplayPriority >= B->DisplayPriority; }); if (!FunctionSaveKey.IsEmpty()) { /* 保存キーを指定してるため、既に一致する情報があればそれをプロパティにセットを試みる */ if (!GetPropertyJsonSystemComponent()->HaveFunctionInJson(FunctionSaveKey, TargetObject, FunctionName.ToString())) { /* 失敗、データがないので現状の値をJsonに書き込み */ GetPropertyJsonSystemComponent()->AddFunctionToJson(FunctionSaveKey, TargetObject, FunctionName.ToString()); } } return true; } UObject* AGameDebugMenuManager::GetObjectProperty(const int32 Index, FGDMGameplayCategoryKey& OutCategoryKey, FString& OutPropertySaveKey, FText& OutDisplayPropertyName, FText& OutDescription, FName& OutPropertyName, EGDMPropertyType& OutPropertyType, FString& OutEnumPathName, FGDMPropertyUIConfigInfo& OutPropertyUIConfigInfo) { OutPropertyType = EGDMPropertyType::GDM_Null; if(!ObjectProperties.IsValidIndex(Index)) { UE_LOG(LogGDM, Warning, TEXT("GetObjectProperty: Not found Index: %d"), Index); return nullptr; } const auto& ObjProp = ObjectProperties[Index]; if(!ObjProp->TargetObject.IsValid()) { UE_LOG(LogGDM, Warning, TEXT("GetObjectProperty: Not found TargetObject")); return nullptr; } const auto Property = ObjProp->TargetProperty; if(Property == nullptr) { UE_LOG(LogGDM, Warning, TEXT("GetObjectProperty: Not found TargetProperty")); return nullptr; } OutCategoryKey = ObjProp->CategoryKey; OutPropertySaveKey = ObjProp->PropertySaveKey; OutDisplayPropertyName = ObjProp->Name; OutDescription = ObjProp->Description; OutPropertyName = ObjProp->PropertyName; OutPropertyUIConfigInfo = ObjProp->ConfigInfo; OutPropertyType = GetPropertyType(Property); if(OutPropertyType == EGDMPropertyType::GDM_Enum) { OutEnumPathName = ObjProp->EnumType->GetPathName(); } else if(OutPropertyType == EGDMPropertyType::GDM_Byte) { if(ObjProp->EnumType.IsValid()) { /* Enumならセット */ OutPropertyType = EGDMPropertyType::GDM_Enum; OutEnumPathName = ObjProp->EnumType->GetPathName(); } } return ObjProp->TargetObject.Get(); } void AGameDebugMenuManager::RemoveObjectProperty(const int32 Index) { ObjectProperties.RemoveAt(Index); } UObject* AGameDebugMenuManager::GetObjectFunction(const int32 Index, FGDMGameplayCategoryKey& OutCategoryKey, FString& OutFunctionSaveKey, FText& OutDisplayFunctionName, FText& OutDescription, FName& OutFunctionName) { if(!ObjectFunctions.IsValidIndex(Index)) { UE_LOG(LogGDM, Warning, TEXT("GetObjectFunction: Not found Index: %d"),Index); return nullptr; } const auto& ObjFunc = ObjectFunctions[Index]; if(!ObjFunc->TargetObject.IsValid()) { UE_LOG(LogGDM, Warning, TEXT("GetObjectFunction: Not found TargetObject")); return nullptr; } const auto& Function = ObjFunc->TargetFunction; if(!Function.IsValid()) { UE_LOG(LogGDM, Warning, TEXT("GetObjectFunction: Not found TargetFunction")); return nullptr; } OutCategoryKey = ObjFunc->CategoryKey; OutDisplayFunctionName = ObjFunc->Name; OutDescription = ObjFunc->Description; OutFunctionName = ObjFunc->FunctionName; OutFunctionSaveKey = ObjFunc->FunctionSaveKey; return ObjFunc->TargetObject.Get(); } void AGameDebugMenuManager::RemoveObjectFunction(const int32 Index) { ObjectFunctions.RemoveAt(Index); } int32 AGameDebugMenuManager::GetNumObjectProperties() const { return ObjectProperties.Num(); } int32 AGameDebugMenuManager::GetNumObjectFunctions() const { return ObjectFunctions.Num(); } UObject* AGameDebugMenuManager::TryGetObjectProperty(const FString& InPropertySaveKey, const FString& InPropertyName, FGDMGameplayCategoryKey& OutCategoryKey, FText& OutDisplayPropertyName, FText& OutDescription, EGDMPropertyType& OutPropertyType, FString& OutEnumPathName, FGDMPropertyUIConfigInfo& OutPropertyUIConfigInfo) const { OutPropertyType = EGDMPropertyType::GDM_Null; for (const auto& ObjProp : ObjectProperties) { if(!ObjProp->TargetObject.IsValid()) { continue; } const auto Property = ObjProp->TargetProperty; if(Property == nullptr) { continue; } if (ObjProp->PropertySaveKey != InPropertySaveKey || ObjProp->PropertyName != InPropertyName) { continue; } OutCategoryKey = ObjProp->CategoryKey; OutDisplayPropertyName = ObjProp->Name; OutDescription = ObjProp->Description; OutPropertyUIConfigInfo = ObjProp->ConfigInfo; OutPropertyType = GetPropertyType(Property); if(OutPropertyType == EGDMPropertyType::GDM_Enum) { OutEnumPathName = ObjProp->EnumType->GetPathName(); } else if(OutPropertyType == EGDMPropertyType::GDM_Byte) { if(ObjProp->EnumType.IsValid()) { /* Enumならセット */ OutPropertyType = EGDMPropertyType::GDM_Enum; OutEnumPathName = ObjProp->EnumType->GetPathName(); } } return ObjProp->TargetObject.Get(); } return nullptr; } UObject* AGameDebugMenuManager::TryGetObjectFunction(const FString& InFunctionSaveKey, const FString& InFunctionName, FGDMGameplayCategoryKey& OutCategoryKey, FText& OutDisplayFunctionName, FText& OutDescription) const { for (const auto& ObjFunc : ObjectFunctions) { if(!ObjFunc->TargetObject.IsValid()) { continue; } const auto& Function = ObjFunc->TargetFunction; if(!Function.IsValid()) { continue; } if (ObjFunc->FunctionSaveKey != InFunctionSaveKey || ObjFunc->FunctionName != InFunctionName) { continue; } OutCategoryKey = ObjFunc->CategoryKey; OutDisplayFunctionName = ObjFunc->Name; OutDescription = ObjFunc->Description; return ObjFunc->TargetObject.Get(); } return nullptr; } void AGameDebugMenuManager::AddDebugMenuPCProxyComponent(APlayerController* PlayerController) { check(IsValid(MenuAsset)); if (!IsValid(MenuAsset->DebugMenuPCProxyComponentClass)) { UE_LOG(LogGDM, Warning, TEXT("AddDebugMenuPCProxyComponent: Not found DebugMenuPCProxyComponentClass")); return; } UGDMPlayerControllerProxyComponent* NewComponent = Cast( PlayerController->AddComponentByClass(MenuAsset->DebugMenuPCProxyComponentClass, false, FTransform::Identity, true)); check(IsValid(NewComponent)); NewComponent->DebugMenuManager = this; PlayerController->FinishAddComponent(NewComponent, false, FTransform::Identity); } void AGameDebugMenuManager::SetIgnoreInput(bool bNewInput) { GetDebugMenuInputSystemComponent()->SetIgnoreInput(bNewInput); } void AGameDebugMenuManager::ResetIgnoreInput() { GetDebugMenuInputSystemComponent()->ResetIgnoreInput(); } bool AGameDebugMenuManager::IsInputIgnored() const { return GetDebugMenuInputSystemComponent()->IsInputIgnored(); } void AGameDebugMenuManager::ChangeDebugMenuLanguage(FName LanguageKey, bool bForcedUpdate) { UE_LOG(LogGDM, Verbose, TEXT("Call ChangeDebugMenuLanguage LanguageKey:%s bForcedUpdate:%d"), *LanguageKey.ToString(), bForcedUpdate); const FName CurrentDebugMenuLanguage = GetLocalizeStringComponent()->GetCurrentDebugMenuLanguage(); if( !bForcedUpdate ) { if( LanguageKey == NAME_None ) { return; } if( CurrentDebugMenuLanguage == LanguageKey ) { return; } } const FName Old = CurrentDebugMenuLanguage; /* 言語を切り替えをして保存。その後テーブルを読み直す */ { GetLocalizeStringComponent()->SetToJsonSystemComponent(GetPropertyJsonSystemComponent(), LanguageKey.ToString()); GetSaveSystemComponent()->SaveDebugMenuFile(); GetLocalizeStringComponent()->SyncLoadDebugMenuStringTables(); } TArray ChildWidgets; for(const auto& ViewportWidget : ViewportDebugMenuWidgets ) { /* Viewportに追加 Widget 内にあるすべてのTextBlockとDebugMenuWidgetを更新 */ { ViewportWidget->GetWidgetChildrenOfClass(UGDMTextBlock::StaticClass(), ChildWidgets, false); for(const auto ChildWidget : ChildWidgets ) { if( UGDMTextBlock* TextBlock = Cast(ChildWidget) ) { if( !TextBlock->DebugMenuStringKey.IsEmpty() ) { TextBlock->SetText(FText::FromString(TextBlock->DebugMenuStringKey)); } } } ViewportWidget->GetWidgetChildrenOfClass(UGameDebugMenuWidget::StaticClass(), ChildWidgets, false); for(const auto ChildWidget : ChildWidgets ) { if( UGameDebugMenuWidget* DebugMenuWidget = Cast(ChildWidget) ) { DebugMenuWidget->OnChangeDebugMenuLanguage(LanguageKey, Old); } } } ViewportWidget->OnChangeDebugMenuLanguage(LanguageKey, Old); } CallChangeDebugMenuLanguageDispatcher(LanguageKey, Old); } TArray AGameDebugMenuManager::GetViewportDebugMenuWidgets() { return ViewportDebugMenuWidgets; } void AGameDebugMenuManager::ChangeConsoleCommandSetAsset(UGDMConsoleCommandSetAsset* NewCommandSetAsset) { ConsoleCommandSetAsset = NewCommandSetAsset; } UGDMConsoleCommandSetAsset* AGameDebugMenuManager::GetConsoleCommandSetAsset() const { return ConsoleCommandSetAsset; } void AGameDebugMenuManager::CallExecuteConsoleCommandDispatcher(const FString& Command) { TArray ListenerComponents; UGDMListenerComponent::GetAllListenerComponents(GetWorld(), ListenerComponents); for(const auto& Component : ListenerComponents) { Component->OnExecuteConsoleCommandDispatcher.Broadcast(Command); } } void AGameDebugMenuManager::CallShowDispatcher() { TArray ListenerComponents; UGDMListenerComponent::GetAllListenerComponents(GetWorld(), ListenerComponents); for(const auto& Component : ListenerComponents) { Component->OnShowDispatcher.Broadcast(); } } void AGameDebugMenuManager::CallHideDispatcher() { TArray ListenerComponents; UGDMListenerComponent::GetAllListenerComponents(GetWorld(), ListenerComponents); for(const auto& Component : ListenerComponents) { Component->OnHideDispatcher.Broadcast(); } } void AGameDebugMenuManager::OnWidgetAdded(UWidget* AddWidget, ULocalPlayer* Player) { if(UGameDebugMenuWidget* W = Cast(AddWidget)) { /* このManagerのOwner LocalPlayerに紐づくWidgetだけを対象にする */ const APlayerController* OwnerPC = Cast(GetOwner()); const ULocalPlayer* OwnerLocalPlayer = OwnerPC ? OwnerPC->GetLocalPlayer() : nullptr; if (IsValid(OwnerLocalPlayer)) { if (IsValid(Player) && Player != OwnerLocalPlayer) { return; } if (W->GetOwningLocalPlayer() != OwnerLocalPlayer) { return; } } if (W->GetWorld() != GetWorld()) { return; } ViewportDebugMenuWidgets.AddUnique(W); } } void AGameDebugMenuManager::OnWidgetRemoved(UWidget* RemoveWidget) { if(UGameDebugMenuWidget* W = Cast(RemoveWidget)) { const APlayerController* OwnerPC = Cast(GetOwner()); const ULocalPlayer* OwnerLocalPlayer = OwnerPC ? OwnerPC->GetLocalPlayer() : nullptr; if (IsValid(OwnerLocalPlayer) && W->GetOwningLocalPlayer() != OwnerLocalPlayer) { return; } if (W->GetWorld() != GetWorld()) { return; } ViewportDebugMenuWidgets.RemoveSingle(W); } } void AGameDebugMenuManager::CallExecuteProcessEventDispatcher(const FName& FunctionName, UObject* TargetObject) { TArray ListenerComponents; UGDMListenerComponent::GetAllListenerComponents(GetWorld(), ListenerComponents); for(const auto& Component : ListenerComponents) { Component->OnExecuteProcessEventDispatcher.Broadcast(FunctionName, TargetObject); } } void AGameDebugMenuManager::CallChangePropertyBoolDispatcher(const FName& PropertyName, UObject* PropertyOwnerObject, bool New, bool Old, const FString& PropertySaveKey) { TArray ListenerComponents; UGDMListenerComponent::GetAllListenerComponents(GetWorld(), ListenerComponents); for(const auto& Component : ListenerComponents) { Component->OnChangePropertyBoolDispatcher.Broadcast(PropertyName, PropertyOwnerObject, New, Old, PropertySaveKey); } } void AGameDebugMenuManager::CallChangePropertyIntDispatcher(const FName& PropertyName, UObject* PropertyOwnerObject, int32 New, int32 Old, const FString& PropertySaveKey) { TArray ListenerComponents; UGDMListenerComponent::GetAllListenerComponents(GetWorld(), ListenerComponents); for(const auto& Component : ListenerComponents) { Component->OnChangePropertyIntDispatcher.Broadcast(PropertyName, PropertyOwnerObject, New, Old, PropertySaveKey); } } void AGameDebugMenuManager::CallChangePropertyFloatDispatcher(const FName& PropertyName, UObject* PropertyOwnerObject, float New, float Old, const FString& PropertySaveKey) { TArray ListenerComponents; UGDMListenerComponent::GetAllListenerComponents(GetWorld(), ListenerComponents); for(const auto& Component : ListenerComponents) { Component->OnChangePropertyFloatDispatcher.Broadcast(PropertyName, PropertyOwnerObject, New, Old, PropertySaveKey); } } void AGameDebugMenuManager::CallChangePropertyByteDispatcher(const FName& PropertyName, UObject* PropertyOwnerObject, uint8 New, uint8 Old, const FString& PropertySaveKey) { TArray ListenerComponents; UGDMListenerComponent::GetAllListenerComponents(GetWorld(), ListenerComponents); for(const auto& Component : ListenerComponents) { Component->OnChangePropertyByteDispatcher.Broadcast(PropertyName, PropertyOwnerObject, New, Old, PropertySaveKey); } } void AGameDebugMenuManager::CallChangePropertyStringDispatcher(const FName& PropertyName, UObject* PropertyOwnerObject, FString New, FString Old, const FString& PropertySaveKey) { TArray ListenerComponents; UGDMListenerComponent::GetAllListenerComponents(GetWorld(), ListenerComponents); for(const auto& Component : ListenerComponents) { Component->OnChangePropertyStringDispatcher.Broadcast(PropertyName, PropertyOwnerObject, New, Old, PropertySaveKey); } } void AGameDebugMenuManager::CallChangePropertyVectorDispatcher(const FName& PropertyName, UObject* PropertyOwnerObject, FVector New, FVector Old, const FString& PropertySaveKey) { TArray ListenerComponents; UGDMListenerComponent::GetAllListenerComponents(GetWorld(), ListenerComponents); for(const auto& Component : ListenerComponents ) { Component->OnChangePropertyVectorDispatcher.Broadcast(PropertyName, PropertyOwnerObject, New, Old, PropertySaveKey); } } void AGameDebugMenuManager::CallChangePropertyVector2DDispatcher(const FName& PropertyName, UObject* PropertyOwnerObject, FVector2D New, FVector2D Old, const FString& PropertySaveKey) { TArray ListenerComponents; UGDMListenerComponent::GetAllListenerComponents(GetWorld(), ListenerComponents); for(const auto& Component : ListenerComponents ) { Component->OnChangePropertyVector2DDispatcher.Broadcast(PropertyName, PropertyOwnerObject, New, Old, PropertySaveKey); } } void AGameDebugMenuManager::CallChangePropertyRotatorDispatcher(const FName& PropertyName, UObject* PropertyOwnerObject, FRotator New, FRotator Old, const FString& PropertySaveKey) { TArray ListenerComponents; UGDMListenerComponent::GetAllListenerComponents(GetWorld(), ListenerComponents); for(const auto& Component : ListenerComponents ) { Component->OnChangePropertyRotatorDispatcher.Broadcast(PropertyName, PropertyOwnerObject, New, Old, PropertySaveKey); } } void AGameDebugMenuManager::CallChangeDebugMenuLanguageDispatcher(const FName& NewLanguageKey, const FName& OldLanguageKey) { TArray ListenerComponents; UGDMListenerComponent::GetAllListenerComponents(GetWorld(), ListenerComponents); for(const auto& Component : ListenerComponents ) { Component->OnChangeDebugMenuLanguageDispatcher.Broadcast(NewLanguageKey, OldLanguageKey); } } void AGameDebugMenuManager::CallStartScreenshotRequestDispatcher() { TArray ListenerComponents; UGDMListenerComponent::GetAllListenerComponents(GetWorld(), ListenerComponents); for(const auto& Component : ListenerComponents ) { Component->OnStartScreenshotRequestDispatcher.Broadcast(); } } void AGameDebugMenuManager::CallScreenshotRequestProcessedDispatcher() { TArray ListenerComponents; UGDMListenerComponent::GetAllListenerComponents(GetWorld(), ListenerComponents); for(const auto& Component : ListenerComponents ) { Component->OnScreenshotRequestProcessedDispatcher.Broadcast(); } } void AGameDebugMenuManager::CallLoadedDebugMenuDispatcher() { TArray ListenerComponents; UGDMListenerComponent::GetAllListenerComponents(GetWorld(), ListenerComponents); for(const auto& Component : ListenerComponents ) { Component->OnLoadedDebugMenuDispatcher.Broadcast(); } } void AGameDebugMenuManager::CallSavedDebugMenuDispatcher() { TArray ListenerComponents; UGDMListenerComponent::GetAllListenerComponents(GetWorld(), ListenerComponents); for(const auto& Component : ListenerComponents ) { Component->OnSavedDebugMenuDispatcher.Broadcast(); } } void AGameDebugMenuManager::CallDeletedDebugMenuDispatcher() { TArray ListenerComponents; UGDMListenerComponent::GetAllListenerComponents(GetWorld(), ListenerComponents); for(const auto& Component : ListenerComponents ) { Component->OnDeletedDebugMenuDispatcher.Broadcast(); } } ``` `SampleProject/Plugins/GameDebugMenu/Source/GameDebugMenu/Private/GameDebugMenuSettings.cpp`: ```cpp /** * Copyright (c) 2020 akihiko moroi * * This software is released under the MIT License. * (See accompanying file LICENSE.txt or copy at http://opensource.org/licenses/MIT) */ #include "GameDebugMenuSettings.h" #include "Performance/EnginePerformanceTargets.h" #include #include "GameDebugMenuTypes.h" #include "Components/Widget.h" #include "Data/GameDebugMenuMasterAsset.h" #include "Engine/AssetManager.h" #include "Input/GDMEnhancedInputComponent.h" #include "Reports/GDMRequesterJira.h" #include "Reports/GDMRequesterRedmine.h" #include "Reports/GDMRequesterTrello.h" UGameDebugMenuSettings::UGameDebugMenuSettings() { CategoryName = TEXT("Plugins"); SetupCategoryResets(); SetupCategorySlomo(); SetupCategoryCamera(); SetupCategoryProfiler(); SetupCategoryDisplay(); SetupCategoryShowDebug(); SetupCategoryViewMode(); SetupCategoryScalability(); SetupCategoryFreeze(); SetupCategoryDumpLogs(); SetupCategoryNetwork(); SetupCategorySound(); SetupCategoryAbilitySystem(); SetupCategoryOther(); SetupCategoryLogVerbosity(); MasterAssetName = TEXT("DA_GDM_Master"); OrderConsoleCommandCategoryTitles.Add(FGDMOrderMenuCategoryTitle(TEXT("Resets"),0)); OrderConsoleCommandCategoryTitles.Add(FGDMOrderMenuCategoryTitle(TEXT("Slomo"),1)); OrderConsoleCommandCategoryTitles.Add(FGDMOrderMenuCategoryTitle(TEXT("Camera"),2)); OrderConsoleCommandCategoryTitles.Add(FGDMOrderMenuCategoryTitle(TEXT("Profiler"),3)); OrderConsoleCommandCategoryTitles.Add(FGDMOrderMenuCategoryTitle(TEXT("Display"),4)); OrderConsoleCommandCategoryTitles.Add(FGDMOrderMenuCategoryTitle(TEXT("Show Debug"),5)); OrderConsoleCommandCategoryTitles.Add(FGDMOrderMenuCategoryTitle(TEXT("ViewMode"),6)); OrderConsoleCommandCategoryTitles.Add(FGDMOrderMenuCategoryTitle(TEXT("Scalability"),7)); OrderConsoleCommandCategoryTitles.Add(FGDMOrderMenuCategoryTitle(TEXT("Freeze"),8)); OrderConsoleCommandCategoryTitles.Add(FGDMOrderMenuCategoryTitle(TEXT("Dump Logs"),9)); OrderConsoleCommandCategoryTitles.Add(FGDMOrderMenuCategoryTitle(TEXT("Network"),10)); OrderConsoleCommandCategoryTitles.Add(FGDMOrderMenuCategoryTitle(TEXT("Sounds"),11)); OrderConsoleCommandCategoryTitles.Add(FGDMOrderMenuCategoryTitle(TEXT("Ability System"),12)); OrderConsoleCommandCategoryTitles.Add(FGDMOrderMenuCategoryTitle(TEXT("Other"),13)); OrderConsoleCommandCategoryTitles.Add(FGDMOrderMenuCategoryTitle(TEXT("Log Verbosity"),14)); OrderGameplayCategoryTitles.Add(FGDMOrderMenuCategoryTitle(TEXT("Other"),0)); /* AGameDebugMenuManagerもデフォルトではInt最大値なのでそれより低くする * 同じ、または大きくした場合、マネージャーで設定する入力はメニューが閉じられるまで反応しなくなるので注意 */ WidgetInputActionPriority = TNumericLimits::Max() - 1; CultureList.Add(TEXT("ja")); CultureList.Add(TEXT("en")); DefaultGameDebugMenuLanguage = TEXT("Japanese"); bDisableScreenCaptureProcessingWhenOpeningDebugMenu = true; bUseSaveGame = false; SaveFilePath = TEXT("Saved/DebugMenu"); SaveFileName = TEXT("DebugMenuSaveData"); bDisableSaveFile = false; bDoesNotSaveConsoleCommand = false; MaxCommandHistoryNum = 100; NoSaveConsoleCommands.Reset(); NoSaveConsoleCommands.Add(TEXT("LevelEditor.")); NoSaveConsoleCommands.Add(TEXT("ToggleDebugCamera")); NoSaveConsoleCommands.Add(TEXT("stat ")); NoSaveConsoleCommands.Add(TEXT("LoadTimes.")); NoSaveConsoleCommands.Add(TEXT("CsvProfile ")); NoSaveConsoleCommands.Add(TEXT("Obj ")); NoSaveConsoleCommands.Add(TEXT("Freeze")); LineBreakString = TEXT("\n"); MasterAsset = nullptr; } #if WITH_EDITOR void UGameDebugMenuSettings::PostEditChangeProperty(struct FPropertyChangedEvent& PropertyChangedEvent) { Super::PostEditChangeProperty(PropertyChangedEvent); int32 Count = INDEX_NONE; for( auto& CategoryTitle : OrderConsoleCommandCategoryTitles ) { CategoryTitle.PreviewTitle = GetDebugMenuString(DefaultGameDebugMenuLanguage, CategoryTitle.Title); if( CategoryTitle.Index > Count ) { Count = CategoryTitle.Index; } } for( auto& CategoryTitle : OrderConsoleCommandCategoryTitles ) { if( CategoryTitle.Index == INDEX_NONE ) { ++Count; CategoryTitle.Index = Count; } } Count = INDEX_NONE; for( auto& CategoryTitle : OrderGameplayCategoryTitles ) { CategoryTitle.PreviewTitle = GetDebugMenuString(DefaultGameDebugMenuLanguage, CategoryTitle.Title); if( CategoryTitle.Index > Count ) { Count = CategoryTitle.Index; } } for( auto& CategoryTitle : OrderGameplayCategoryTitles ) { if( CategoryTitle.Index == INDEX_NONE ) { ++Count; CategoryTitle.Index = Count; } } } FText UGameDebugMenuSettings::GetSectionText() const { return FText::FromString(TEXT("Game Debug Menu")); } #endif TArray UGameDebugMenuSettings::GetIssueCategoryNameList() const { switch (ProjectManagementToolType) { case EGDMProjectManagementTool::Trello: { return TrelloSettings.CardListNames; } case EGDMProjectManagementTool::Redmine: { return RedmineSettings.TrackerNameList; } case EGDMProjectManagementTool::Jira: { return JiraSettings.IssueTypeList; } } TArray Empty; return Empty; } TArray UGameDebugMenuSettings::GetPriorityNameList() const { switch (ProjectManagementToolType) { case EGDMProjectManagementTool::Trello: { break;/* 特になし */ } case EGDMProjectManagementTool::Redmine: { return RedmineSettings.PriorityNameList; } case EGDMProjectManagementTool::Jira: { return JiraSettings.PriorityNameList; } } TArray Empty; return Empty; } TArray UGameDebugMenuSettings::GetAssigneeNameList() const { switch(ProjectManagementToolType) { case EGDMProjectManagementTool::Trello: { /* 未対応 */ break; } case EGDMProjectManagementTool::Redmine: { /* 未対応 */ break; } case EGDMProjectManagementTool::Jira: { TArray TextList; JiraSettings.AssigneeList.GenerateValueArray(TextList); return TextList; } } TArray Empty; return Empty; } int32 UGameDebugMenuSettings::GetDefaultIssueCategoryIndex() const { switch (ProjectManagementToolType) { case EGDMProjectManagementTool::Trello: { break;/* 特になし */ } case EGDMProjectManagementTool::Redmine: { return RedmineSettings.DefaultTrackerIndex; } case EGDMProjectManagementTool::Jira: { return JiraSettings.DefaultIssueTypeIndex; } } return 0; } int32 UGameDebugMenuSettings::GetDefaultPriorityIndex() const { switch (ProjectManagementToolType) { case EGDMProjectManagementTool::Trello: { break;/* 特になし */ } case EGDMProjectManagementTool::Redmine: { return RedmineSettings.DefaultPriorityIndex; } case EGDMProjectManagementTool::Jira: { return JiraSettings.DefaultPriorityIndex; } } return 0; } FString UGameDebugMenuSettings::GetGameplayCategoryTitle(const int32& ArrayIndex) const { FString ReturnValue; if( OrderGameplayCategoryTitles.IsValidIndex(ArrayIndex) ) { const FString& Title = OrderGameplayCategoryTitles[ArrayIndex].Title; ReturnValue = GetDebugMenuString(DefaultGameDebugMenuLanguage, Title); if( ReturnValue.IsEmpty() ) { ReturnValue = Title; } } return ReturnValue; } int32 UGameDebugMenuSettings::GetGameplayCategoryIndex(const int32& ArrayIndex) const { if( OrderGameplayCategoryTitles.IsValidIndex(ArrayIndex) ) { return OrderGameplayCategoryTitles[ArrayIndex].Index; } return 0; } FString UGameDebugMenuSettings::GetFullSavePath() const { return FPaths::ProjectDir().Append(SaveFilePath).Append(TEXT("/")).Append(SaveFileName).Append(TEXT(".json")); } const FGDMStringTableList* UGameDebugMenuSettings::TryGetStringTableList(const FName& LanguageKey) const { if (const auto Master = GetMasterAsset()) { return Master->GameDebugMenuStringTables.Find(LanguageKey); } return nullptr; } TArray UGameDebugMenuSettings::GetDebugMenuLanguageKeys() const { TArray ReturnValues; if (const auto Master = GetMasterAsset()) { Master->GameDebugMenuStringTables.GetKeys(ReturnValues); } return ReturnValues; } UClass* UGameDebugMenuSettings::GetDebugMenuInputComponentClass() const { TSoftClassPtr Class = nullptr; if (const auto Master = GetMasterAsset()) { Class = Master->DebugMenuInputComponentClass; } ensureMsgf(Class.IsValid(), TEXT("Invalid DebugMenuInputComponentClass class in GameDebugMenuSettings. Manual reset required.")); return Class.IsValid() ? Class.Get() : UGDMEnhancedInputComponent::StaticClass(); } const TSubclassOf* UGameDebugMenuSettings::GetDebugReportRequesterClass() const { if (const auto Master = GetMasterAsset()) { return Master->DebugReportRequesterClass.Find(ProjectManagementToolType); } return nullptr; } FString UGameDebugMenuSettings::GetDebugMenuString(const FName& LanguageKey, const FString& StringKey) const { if( const FGDMStringTableList* StringTableList = TryGetStringTableList(LanguageKey) ) { FString OutSourceString; for( auto& StrTablePtr : StringTableList->StringTables ) { if( !StrTablePtr.ToSoftObjectPath().IsValid() || StrTablePtr.ToSoftObjectPath().IsNull() ) { UE_LOG(LogGDM, Warning, TEXT("GetDebugMenuString: failed StringTable : LanguageKey->%s StringKey->%s"), *LanguageKey.ToString(), *StringKey); continue; } if(const UStringTable* StringTable = StrTablePtr.LoadSynchronous() ) { const FStringTableConstPtr TableData = StringTable->GetStringTable(); if (!TableData.IsValid() || !TableData->IsLoaded()) { UE_LOG(LogGDM, Warning, TEXT("GetDebugMenuString: StringTable not loaded: %s"), *StringTable->GetName()); continue; } if( TableData->GetSourceString(StringKey, OutSourceString) ) { return OutSourceString; } } } } return FString(); } UObject* UGameDebugMenuSettings::GetDebugMenuFont() const { if (const auto Master = GetMasterAsset()) { if (UObject* FontObj = Master->FontName.ResolveObject()) { return FontObj; } return Master->FontName.TryLoad(); } const FSoftObjectPath EngineFontPath = UWidget::GetDefaultFontName(); return EngineFontPath.TryLoad(); } UGameDebugMenuMasterAsset* UGameDebugMenuSettings::GetMasterAsset() const { const UAssetManager* AssetManager = UAssetManager::GetIfInitialized(); if (!IsValid(AssetManager)) { return nullptr; } if (MasterAssetName.IsEmpty()) { UE_LOG(LogGDM, Warning, TEXT("MasterAssetName is empty.")); return nullptr; } const FPrimaryAssetId MasterAssetId(UGameDebugMenuMasterAsset::GetPrimaryType(), FName(*MasterAssetName)); if (IsValid(MasterAsset)) { const FPrimaryAssetId CachedId = MasterAsset->GetPrimaryAssetId(); if (CachedId == MasterAssetId) { return MasterAsset.Get(); } } UObject* Asset = AssetManager->GetPrimaryAssetObject(MasterAssetId); if (!IsValid(Asset)) { const FSoftObjectPath AssetPath = AssetManager->GetPrimaryAssetPath(MasterAssetId); Asset = AssetPath.TryLoad(); } MasterAsset = Cast(Asset); return MasterAsset; } void UGameDebugMenuSettings::SetupCategoryResets() { FGDMConsoleCommandSingle Single; Single.CategoryIndex = 0; /* Resets */ Single.CommandNetType = EGDMConsoleCommandNetType::LocalOnly; Single.Title = FText::FromString(TEXT("Stat None")); Single.Description = FText::FromString(TEXT("Statをクリアする")); Single.ConsoleCommandName = TEXT("stat None"); ConsoleCommandNames.Add(Single); Single.Title = FText::FromString(TEXT("Grouped")); Single.Description = FText::FromString(TEXT("stat Slow を無効")); Single.ConsoleCommandName = TEXT("stat Grouped"); ConsoleCommandNames.Add(Single); Single.Title = FText::FromString(TEXT("Load Times Report Reset")); Single.Description = FText::FromString(TEXT("loadtimes.dumpreportの出力情報をリセットする")); Single.ConsoleCommandName = TEXT("LoadTimes.reset"); ConsoleCommandNames.Add(Single); FGDMConsoleCommandPair Pair; Pair.CategoryIndex = 0; /* Resets */ Pair.CommandNetType = EGDMConsoleCommandNetType::LocalOnly; Pair.Title = FText::FromString(TEXT("Toggle All Screen Messages")); Pair.Description = FText::FromString(TEXT("トグルで画面ログの(非)表示を切り替える")); Pair.ClickedEvent = EGDMConsoleCommandClickedEvent::Non; Pair.FirstConsoleCommandName = TEXT("DisableAllScreenMessages"); Pair.SecondConsoleCommandName = TEXT("EnableAllScreenMessages"); ConsoleCommandPairs.Add(Pair); } void UGameDebugMenuSettings::SetupCategorySlomo() { FGDMConsoleCommandSingle Single; Single.CategoryIndex = 1; /* Slomo */ Single.Title = FText::FromString(TEXT("Reset")); Single.Description = FText::FromString(TEXT("Slomoを1にリセットする")); Single.ConsoleCommandName = TEXT("Slomo 1"); Single.CommandNetType = EGDMConsoleCommandNetType::ServerAll; ConsoleCommandNames.Add(Single); FGDMConsoleCommandNumber Number; Number.CategoryIndex = 1; /* Slomo */ Number.PreConsoleCommandName = TEXT(""); Number.PostConsoleCommandName = TEXT(" "); Number.Title = FText::FromString(TEXT("Change Slomo")); Number.Description = FText::FromString(TEXT("Slomoを変更し反映させる")); Number.ConsoleCommandName = TEXT("Slomo"); Number.ConsoleVariableName = TEXT("Slomo"); Number.UIConfigInfo.Range.bUseMax = true; Number.UIConfigInfo.Range.bUseMin = true; Number.UIConfigInfo.Range.MinValue = 0.0f; Number.UIConfigInfo.Range.MaxValue = 10.0f; Number.UIConfigInfo.DefaultChangeAmount = 0.1f; Number.UIConfigInfo.MaxChangeAmount = 0.2f; Number.DefaultValue = 1.0f; Number.CommandNetType = EGDMConsoleCommandNetType::ServerAll; ConsoleCommandNumbers.Add(Number); } void UGameDebugMenuSettings::SetupCategoryCamera() { FGDMConsoleCommandSingle Single; Single.CategoryIndex = 2; /* Camera */ Single.CommandNetType = EGDMConsoleCommandNetType::LocalOnly; Single.Title = FText::FromString(TEXT("Debug Camera")); Single.Description = FText::FromString(TEXT("ゲーム中のカメラを離れ、デバッグ用の別カメラに切り替える\n同時に注視点のアセットの情報を画面に表示します")); Single.ClickedEvent = EGDMConsoleCommandClickedEvent::MenuClose; Single.ConsoleCommandName = TEXT("ToggleDebugCamera"); ConsoleCommandNames.Add(Single); Single.CommandNetType = EGDMConsoleCommandNetType::ServerAll; Single.Title = FText::FromString(TEXT("Teleport Player controlled pawn")); Single.Description = FText::FromString(TEXT("プレイヤー操作ポーンをカメラの注視点にテレポートさせる")); Single.ClickedEvent = EGDMConsoleCommandClickedEvent::Non; Single.ConsoleCommandName = TEXT("Teleport"); ConsoleCommandNames.Add(Single); FGDMConsoleCommandPair Pair; Pair.CategoryIndex = 2; /* Camera */ Pair.CommandNetType = EGDMConsoleCommandNetType::LocalOnly; Pair.Title = FText::FromString(TEXT("Debug Camera Trace Complex")); Pair.Description = FText::FromString(TEXT("")); Pair.ClickedEvent = EGDMConsoleCommandClickedEvent::Non; Pair.FirstConsoleCommandName = TEXT("g.DebugCameraTraceComplex 0"); Pair.SecondConsoleCommandName = TEXT("g.DebugCameraTraceComplex 1"); ConsoleCommandPairs.Add(Pair); } void UGameDebugMenuSettings::SetupCategoryProfiler() { FGDMConsoleCommandSingle Single; Single.CategoryIndex = 3; /* Profiler */ Single.CommandNetType = EGDMConsoleCommandNetType::LocalOnly; Single.Title = FText::FromString(TEXT("FPS")); Single.Description = FText::FromString(TEXT("")); Single.ConsoleCommandName = TEXT("stat fps"); ConsoleCommandNames.Add(Single); Single.Title = FText::FromString(TEXT("Unit")); Single.Description = FText::FromString(TEXT("")); Single.ConsoleCommandName = TEXT("stat Unit"); ConsoleCommandNames.Add(Single); Single.Title = FText::FromString(TEXT("Unit Graph")); Single.Description = FText::FromString(TEXT("")); Single.ConsoleCommandName = TEXT("stat UnitGraph"); ConsoleCommandNames.Add(Single); Single.Title = FText::FromString(TEXT("Unit Max")); Single.Description = FText::FromString(TEXT("")); Single.ConsoleCommandName = TEXT("stat UnitMax"); ConsoleCommandNames.Add(Single); Single.Title = FText::FromString(TEXT("Unit Time")); Single.Description = FText::FromString(TEXT("")); Single.ConsoleCommandName = TEXT("stat UnitTime"); ConsoleCommandNames.Add(Single); Single.Title = FText::FromString(TEXT("Raw")); Single.Description = FText::FromString(TEXT("")); Single.ConsoleCommandName = TEXT("stat Raw"); ConsoleCommandNames.Add(Single); FGDMConsoleCommandGroup Group; Group.CategoryIndex = 3; /* Profiler */ Group.CommandNetType = EGDMConsoleCommandNetType::LocalOnly; /* EditorOnly */ Group.Title = FText::FromString(TEXT("ProfileGPU ShowUI")); Group.Description = FText::FromString(TEXT("GPUの処理負荷を階層的に出力(エディター用UI表示版)\nエディターでのみ有効")); Group.ConsoleCommandNames.Add(TEXT("r.ProfileGPU.ShowUI 1")); Group.ConsoleCommandNames.Add(TEXT("ProfileGPU")); EditorOnlyConsoleCommandGroups.Add(Group); Group.ConsoleCommandNames.Empty(); /* EditorOnly */ Group.Title = FText::FromString(TEXT("ProfileGPU")); Group.Description = FText::FromString(TEXT("GPUの処理負荷を階層的に出力")); Group.ConsoleCommandNames.Add(TEXT("r.ProfileGPU.ShowUI 0")); Group.ConsoleCommandNames.Add(TEXT("ProfileGPU")); ConsoleCommandGroups.Add(Group); Group.ConsoleCommandNames.Empty(); FGDMConsoleCommandPair Pair; Pair.CategoryIndex = 3; /* Profiler */ Pair.CommandNetType = EGDMConsoleCommandNetType::LocalOnly; Pair.Title = FText::FromString(TEXT("Toggle RHISetGPUCaptureOptions")); Pair.Description = FText::FromString(TEXT("BasePass内部の各DrawCall毎の処理負荷を出力する\n(マテリアル名などが見られるようになったり) ※RHIThreadがOffになり、DrawThread負荷が増加する点に注意")); Pair.ClickedEvent = EGDMConsoleCommandClickedEvent::Non; Pair.FirstConsoleCommandName = TEXT("r.RHISetGPUCaptureOptions 1"); Pair.SecondConsoleCommandName = TEXT("r.RHISetGPUCaptureOptions 0"); ConsoleCommandPairs.Add(Pair); Pair.Title = FText::FromString(TEXT("Toggle Profile")); Pair.Description = FText::FromString(TEXT("startfile / stopfile をトグルで実行。プロファイル結果をファイルにue4stats形式で出力する")); Pair.ClickedEvent = EGDMConsoleCommandClickedEvent::MenuClose; Pair.FirstConsoleCommandName = TEXT("stat startfile"); Pair.SecondConsoleCommandName = TEXT("stat stopfile"); ConsoleCommandPairs.Add(Pair); Pair.Title = FText::FromString(TEXT("Toggle CSV Profiler")); Pair.Description = FText::FromString(TEXT("CsvProfile Start / Stop のトグル")); Pair.ClickedEvent = EGDMConsoleCommandClickedEvent::MenuClose; Pair.FirstConsoleCommandName = TEXT("CsvProfile Start"); Pair.SecondConsoleCommandName = TEXT("CsvProfile Stop"); ConsoleCommandPairs.Add(Pair); } void UGameDebugMenuSettings::SetupCategoryDisplay() { FGDMConsoleCommandSingle Single; Single.CategoryIndex = 4; /* Display */ Single.CommandNetType = EGDMConsoleCommandNetType::LocalOnly; Single.Title = FText::FromString(TEXT("Game")); Single.Description = FText::FromString(TEXT("各種 Gameplay のティックの所要時間に関するフィードバックです")); Single.ConsoleCommandName = TEXT("stat Game"); ConsoleCommandNames.Add(Single); Single.Title = FText::FromString(TEXT("Engine")); Single.Description = FText::FromString(TEXT("フレーム時間やレンダリング中のトライアングル数のカウンタなど\n一般的なレンダリング統計情報を表示します")); Single.ConsoleCommandName = TEXT("stat Engine"); ConsoleCommandNames.Add(Single); Single.Title = FText::FromString(TEXT("Init Views")); Single.Description = FText::FromString(TEXT("カリングの所要時間やプリミティブ数などの情報を表示する")); Single.ConsoleCommandName = TEXT("stat InitViews"); ConsoleCommandNames.Add(Single); Single.Title = FText::FromString(TEXT("Scene Rendering")); Single.Description = FText::FromString(TEXT("一般的なレンダリング統計を示す")); Single.ConsoleCommandName = TEXT("stat SceneRendering"); ConsoleCommandNames.Add(Single); Single.Title = FText::FromString(TEXT("GPU")); Single.Description = FText::FromString(TEXT("ProfileGPUのリアルタイム版。GPUのフレーム単位の統計を表示する")); Single.ConsoleCommandName = TEXT("stat GPU"); ConsoleCommandNames.Add(Single); Single.Title = FText::FromString(TEXT("RHI")); Single.Description = FText::FromString(TEXT("全体のDrawCall数などを確認できる")); Single.ConsoleCommandName = TEXT("stat RHI"); ConsoleCommandNames.Add(Single); Single.Title = FText::FromString(TEXT("Particles")); Single.Description = FText::FromString(TEXT("パーティクルの統計情報。今どのくらい出ていて、どのくらい処理がかかっているのかを表示する")); Single.ConsoleCommandName = TEXT("stat GPUParticles"); ConsoleCommandNames.Add(Single); Single.Title = FText::FromString(TEXT("Levels")); Single.Description = FText::FromString(TEXT("現在読み込まれているLevelの表示。読み込みにかかった時間も表示される")); Single.ConsoleCommandName = TEXT("stat Levels"); ConsoleCommandNames.Add(Single); Single.Title = FText::FromString(TEXT("LightRendering")); Single.Description = FText::FromString(TEXT("ライティングとシャドウのレンダリングにかかる時間に関する情報を表示")); Single.ConsoleCommandName = TEXT("stat LightRendering"); ConsoleCommandNames.Add(Single); Single.Title = FText::FromString(TEXT("ShadowRendering")); Single.Description = FText::FromString(TEXT("stat LightRendering で示される実際のシャドウ レンダリング時間とは別に\nシャドウの計算にかかっている時間を表示")); Single.ConsoleCommandName = TEXT("stat ShadowRendering"); ConsoleCommandNames.Add(Single); Single.Title = FText::FromString(TEXT("Streaming")); Single.Description = FText::FromString(TEXT("テクスチャのストリーミング処理で使用しているメモリ量、シーン内に存在するストリーミング中のテクスチャ数など\nストリーミング中のアセットの各種統計情報を表示")); Single.ConsoleCommandName = TEXT("stat Streaming"); ConsoleCommandNames.Add(Single); Single.Title = FText::FromString(TEXT("Streaming Details")); Single.Description = FText::FromString(TEXT("一般的なテクスチャ ストリーミングをさらに特定のグループ\n (ライトマップ、静的 テクスチャ、動的テクスチャ) に分類するなど\nストリーミングに関するより詳しい統計情報を表示")); Single.ConsoleCommandName = TEXT("stat StreamingDetails"); ConsoleCommandNames.Add(Single); Single.Title = FText::FromString(TEXT("Memory")); Single.Description = FText::FromString(TEXT("メモリ使用量の統計を表示する")); Single.ConsoleCommandName = TEXT("stat Memory"); ConsoleCommandNames.Add(Single); Single.Title = FText::FromString(TEXT("Animation")); Single.Description = FText::FromString(TEXT("SkeletalMeshなどのティックごとの計算時間を表示する")); Single.ConsoleCommandName = TEXT("stat Anim"); ConsoleCommandNames.Add(Single); Single.Title = FText::FromString(TEXT("Slate")); Single.Description = FText::FromString(TEXT("Slateで使用するTickなどを表示する")); Single.ConsoleCommandName = TEXT("stat Slate"); ConsoleCommandNames.Add(Single); Single.Title = FText::FromString(TEXT("Hitches")); Single.Description = FText::FromString(TEXT("ヒッチを検知してログに出力する")); Single.ConsoleCommandName = TEXT("stat hitches"); ConsoleCommandNames.Add(Single); Single.Title = FText::FromString(TEXT("Character")); Single.Description = FText::FromString(TEXT("")); Single.ConsoleCommandName = TEXT("stat character"); ConsoleCommandNames.Add(Single); Single.Title = FText::FromString(TEXT("GameplayTags")); Single.Description = FText::FromString(TEXT("")); Single.ConsoleCommandName = TEXT("stat GameplayTags"); ConsoleCommandNames.Add(Single); Single.Title = FText::FromString(TEXT("Collision")); Single.Description = FText::FromString(TEXT("")); Single.ConsoleCommandName = TEXT("stat Collision"); ConsoleCommandNames.Add(Single); Single.Title = FText::FromString(TEXT("Component")); Single.Description = FText::FromString(TEXT("コンポーネントのパフォーマンス情報(Transform更新など)リストを表示する")); Single.ConsoleCommandName = TEXT("stat component"); ConsoleCommandNames.Add(Single); Single.Title = FText::FromString(TEXT("LLM")); Single.Description = FText::FromString(TEXT("Low Level Memory Tracker(LLM)はメモリの使用状況を追跡するツールを使用し\nTag付けされた使用中のメモリを表示\n(LLM有効にはならないので事前に設定すること)")); Single.ConsoleCommandName = TEXT("stat llm"); ConsoleCommandNames.Add(Single); Single.Title = FText::FromString(TEXT("LLMPlatform")); Single.Description = FText::FromString(TEXT("OSの情報を含むメモリ情報を表示")); Single.ConsoleCommandName = TEXT("stat LLMPlatform"); ConsoleCommandNames.Add(Single); Single.Title = FText::FromString(TEXT("UObjects")); Single.Description = FText::FromString(TEXT("ゲーム内の UObjects のパフォーマンスに関する統計情報を表示")); Single.ConsoleCommandName = TEXT("stat UObjects"); ConsoleCommandNames.Add(Single); FGDMConsoleCommandGroup Group; Group.CategoryIndex = 4; /* Display */ Group.CommandNetType = EGDMConsoleCommandNetType::LocalOnly; Group.Title = FText::FromString(TEXT("AI Info")); Group.Description = FText::FromString(TEXT("AIに必要な情報(BehaviorTree,EQS)をまとめて表示する")); Group.ConsoleCommandNames.Add(TEXT("stat AI")); Group.ConsoleCommandNames.Add(TEXT("stat AIBehaviorTree")); Group.ConsoleCommandNames.Add(TEXT("stat AI_EQS")); Group.ConsoleCommandNames.Add(TEXT("stat Navigation")); ConsoleCommandGroups.Add(Group); Group.ConsoleCommandNames.Empty(); Group.Title = FText::FromString(TEXT("All Memory Info")); Group.Description = FText::FromString(TEXT("使用量、他アロケーター情報などを一括表示する")); Group.ConsoleCommandNames.Add(TEXT("stat MemoryPlatform")); Group.ConsoleCommandNames.Add(TEXT("stat MemoryStaticMesh")); Group.ConsoleCommandNames.Add(TEXT("stat MemoryAllocator")); ConsoleCommandGroups.Add(Group); Group.ConsoleCommandNames.Empty(); } void UGameDebugMenuSettings::SetupCategoryShowDebug() { FGDMConsoleCommandPair Pair; Pair.CategoryIndex = 5; /* Show Debug */ Pair.CommandNetType = EGDMConsoleCommandNetType::LocalOnly; Pair.Title = FText::FromString(TEXT("Toggle VisualizeMovement")); Pair.Description = FText::FromString(TEXT("キャラクターの加速度や移動モードを表示させる")); Pair.ClickedEvent = EGDMConsoleCommandClickedEvent::Non; Pair.FirstConsoleCommandName = TEXT("p.VisualizeMovement 1"); Pair.SecondConsoleCommandName = TEXT("p.VisualizeMovement 0"); ConsoleCommandPairs.Add(Pair); Pair.Title = FText::FromString(TEXT("Toggle VisualizeLODs")); Pair.Description = FText::FromString(TEXT("SkinnedMeshComponentで適応してるLODの確認\nボーン数、頂点数などが確認可能(Shipping / Test以外のビルドで有効)\nLOD0:White LOD1:Green LOD2:Yellow LOD3:Red Other:Purple")); Pair.ClickedEvent = EGDMConsoleCommandClickedEvent::Non; Pair.FirstConsoleCommandName = TEXT("a.VisualizeLODs 1"); Pair.SecondConsoleCommandName = TEXT("a.VisualizeLODs 0"); ConsoleCommandPairs.Add(Pair); Pair.Title = FText::FromString(TEXT("Toggle VisualizeOccludedPrimitives")); Pair.Description = FText::FromString(TEXT("オクルードされたアクタを視覚化する")); Pair.ClickedEvent = EGDMConsoleCommandClickedEvent::Non; Pair.FirstConsoleCommandName = TEXT("r.VisualizeOccludedPrimitives 1"); Pair.SecondConsoleCommandName = TEXT("r.VisualizeOccludedPrimitives 0"); ConsoleCommandPairs.Add(Pair); Pair.Title = FText::FromString(TEXT("Toggle LODColoration")); Pair.Description = FText::FromString(TEXT("PrimitiveComponentのLODレベル毎の色で確認\nLOD0:White LOD1:Red LOD2:Green LOD3:Blue LOD4:Yellow LOD5:Fuchisia LOD6:Cyan LOD7:Purple")); Pair.ClickedEvent = EGDMConsoleCommandClickedEvent::Non; Pair.FirstConsoleCommandName = TEXT("ShowFlag.LODColoration 1"); Pair.SecondConsoleCommandName = TEXT("ShowFlag.LODColoration 0"); ConsoleCommandPairs.Add(Pair); FGDMConsoleCommandSingle Single; Single.CategoryIndex = 5; /* Show Debug */ Single.CommandNetType = EGDMConsoleCommandNetType::LocalOnly; Single.Title = FText::FromString(TEXT("Toggle ShowDebug")); Single.Description = FText::FromString(TEXT("HUDを使用したデバック情報を画面に表示するかを切り替える")); Single.ConsoleCommandName = TEXT("ShowDebug"); ConsoleCommandNames.Add(Single); Single.Title = FText::FromString(TEXT("ShowDebug NextDebugTarget")); Single.Description = FText::FromString(TEXT("デバック情報の表示対象アクターを切り替える(次へ進む)")); Single.ConsoleCommandName = TEXT("NextDebugTarget"); ConsoleCommandNames.Add(Single); Single.Title = FText::FromString(TEXT("ShowDebug PreviousDebugTarget")); Single.Description = FText::FromString(TEXT("デバック情報の表示対象アクターを切り替える(戻る)")); Single.ConsoleCommandName = TEXT("PreviousDebugTarget"); ConsoleCommandNames.Add(Single); Single.Title = FText::FromString(TEXT("ShowDebug Reset")); Single.Description = FText::FromString(TEXT("HUDを使用したデバック情報の表示項目をリセットする")); Single.ConsoleCommandName = TEXT("ShowDebug Reset"); ConsoleCommandNames.Add(Single); Single.Title = FText::FromString(TEXT("ShowDebug Input")); Single.Description = FText::FromString(TEXT("入力情報を表示する")); Single.ConsoleCommandName = TEXT("ShowDebug Input"); ConsoleCommandNames.Add(Single); Single.Title = FText::FromString(TEXT("ShowDebug Enhancedinput")); Single.Description = FText::FromString(TEXT("Enhancedinputの入力情報を表示する")); Single.ConsoleCommandName = TEXT("Showdebug Enhancedinput"); ConsoleCommandNames.Add(Single); Single.Title = FText::FromString(TEXT("ShowDebug Collision")); Single.Description = FText::FromString(TEXT("対象アクターのコリジョン情報を表示する")); Single.ConsoleCommandName = TEXT("ShowDebug COLLISION"); ConsoleCommandNames.Add(Single); Single.Title = FText::FromString(TEXT("ShowDebug Physics")); Single.Description = FText::FromString(TEXT("対象アクターのPhysics関係の情報を表示する")); Single.ConsoleCommandName = TEXT("ShowDebug PHYSICS"); ConsoleCommandNames.Add(Single); Single.Title = FText::FromString(TEXT("ShowDebug Forcefeedback")); Single.Description = FText::FromString(TEXT("")); Single.ConsoleCommandName = TEXT("ShowDebug FORCEFEEDBACK"); ConsoleCommandNames.Add(Single); Single.Title = FText::FromString(TEXT("ShowDebug Camera")); Single.Description = FText::FromString(TEXT("アクティブなカメラの情報を表示する")); Single.ConsoleCommandName = TEXT("ShowDebug CAMERA"); ConsoleCommandNames.Add(Single); Single.Title = FText::FromString(TEXT("ShowDebug Animation")); Single.Description = FText::FromString(TEXT("対象アクターのSkeletalMeshのアニメーション情報を表示する")); Single.ConsoleCommandName = TEXT("ShowDebug ANIMATION"); ConsoleCommandNames.Add(Single); Single.Title = FText::FromString(TEXT("ShowDebug Animation Toggle Graph")); Single.Description = FText::FromString(TEXT("アニムグラフの内容を表示を切り替える")); Single.ConsoleCommandName = TEXT("ShowDebugToggleSubCategory GRAPH"); ConsoleCommandNames.Add(Single); Single.Title = FText::FromString(TEXT("ShowDebug Animation Toggle FullGraph")); Single.Description = FText::FromString(TEXT("アニムグラフの内容をすべて表示するかを切り替える")); Single.ConsoleCommandName = TEXT("ShowDebugToggleSubCategory FULLGRAPH"); ConsoleCommandNames.Add(Single); Single.Title = FText::FromString(TEXT("ShowDebug Animation Toggle Curves")); Single.Description = FText::FromString(TEXT("Curve情報を表示するか切り替える")); Single.ConsoleCommandName = TEXT("ShowDebugToggleSubCategory CURVES"); ConsoleCommandNames.Add(Single); Single.Title = FText::FromString(TEXT("ShowDebug Animation Toggle Montages")); Single.Description = FText::FromString(TEXT("モンタージュ情報を表示するか切り替える")); Single.ConsoleCommandName = TEXT("ShowDebugToggleSubCategory MONTAGES"); ConsoleCommandNames.Add(Single); Single.Title = FText::FromString(TEXT("ShowDebug Animation Toggle Notifies")); Single.Description = FText::FromString(TEXT("アニメーション通知情報を表示するか切り替える")); Single.ConsoleCommandName = TEXT("ShowDebugToggleSubCategory NOTIFIES"); ConsoleCommandNames.Add(Single); Single.Title = FText::FromString(TEXT("ShowDebug Animation Toggle SyncGroups")); Single.Description = FText::FromString(TEXT("SyncGroup情報を表示するか切り替える")); Single.ConsoleCommandName = TEXT("ShowDebugToggleSubCategory SYNCGROUPS"); ConsoleCommandNames.Add(Single); Single.Title = FText::FromString(TEXT("ShowDebug Animation Toggle FullBlendSpaceDisplay")); Single.Description = FText::FromString(TEXT("ブレンドスペースの重み値の表示を切り替える(SYNCGROUPSが表示されるとき反映)")); Single.ConsoleCommandName = TEXT("ShowDebugToggleSubCategory FULLBLENDSPACEDISPLAY"); ConsoleCommandNames.Add(Single); } void UGameDebugMenuSettings::SetupCategoryViewMode() { FGDMConsoleCommandSingle Single; Single.CategoryIndex = 6; /* ViewMode */ Single.CommandNetType = EGDMConsoleCommandNetType::LocalOnly; Single.Title = FText::FromString(TEXT("Lit")); Single.Description = FText::FromString(TEXT("シーン全てのライティングがある状態\nエディターでかつStandaloneのみ有効\n(Package環境で使用したい場合は「r.ForceDebugViewModes」を1に)")); Single.ConsoleCommandName = TEXT("viewmode lit"); ConsoleCommandNames.Add(Single); Single.Title = FText::FromString(TEXT("Unlit")); Single.Description = FText::FromString(TEXT("シーン全てのライティングを切った状態\nエディターでかつStandaloneのみ有効\n(Package環境で使用したい場合は「r.ForceDebugViewModes」を1に)")); Single.ConsoleCommandName = TEXT("viewmode unlit"); ConsoleCommandNames.Add(Single); Single.Title = FText::FromString(TEXT("Wireframe")); Single.Description = FText::FromString(TEXT("シーン内のすべてのポリゴンエッジを表示")); Single.ConsoleCommandName = TEXT("viewmode Wireframe"); ConsoleCommandNames.Add(Single); Single.Title = FText::FromString(TEXT("Detaillighting")); Single.Description = FText::FromString(TEXT("シーン全体に中間色のマテリアルをアクティベートする\n基本色が暗すぎたり、ライティングが不明瞭になっている場合の分離に有効\nエディターでかつStandaloneのみ有効\n(Package環境で使用したい場合は「r.ForceDebugViewModes」を1に)")); Single.ConsoleCommandName = TEXT("viewmode lit_detaillighting"); ConsoleCommandNames.Add(Single); Single.Title = FText::FromString(TEXT("Lighting Only")); Single.Description = FText::FromString(TEXT("ライティングにのみ影響を受ける中間色のマテリアルを表示\nエディターでかつStandaloneのみ有効\n(Package環境で使用したい場合は「r.ForceDebugViewModes」を1に)")); Single.ConsoleCommandName = TEXT("viewmode lightingonly"); ConsoleCommandNames.Add(Single); Single.Title = FText::FromString(TEXT("Reflection")); Single.Description = FText::FromString(TEXT("反射の表示\nエディターでかつStandaloneのみ有効\n(Package環境で使用したい場合は「r.ForceDebugViewModes」を1に)")); Single.ConsoleCommandName = TEXT("viewmode ReflectionOverride"); ConsoleCommandNames.Add(Single); Single.Title = FText::FromString(TEXT("Light Complexity")); Single.Description = FText::FromString(TEXT("ジオメトリに影響を与える非静的ライトの数を表示\nライトがサーフェスに影響を与えるほど、描画負荷が高くなる\nエディターでかつStandaloneのみ有効\n(Package環境で使用したい場合は「r.ForceDebugViewModes」を1に)")); Single.ConsoleCommandName = TEXT("viewmode lightcomplexity"); ConsoleCommandNames.Add(Single); Single.Title = FText::FromString(TEXT("Lightmap Density")); Single.Description = FText::FromString(TEXT("テクスチャ マッピングされるオブジェクトのライトマップ密度を表示\n理想的/最大限の密度設定別に色分けして、実際のライトマップ テクセルへマッピングするグリッドを表示\nエディターでかつStandaloneのみ有効\n(Package環境で使用したい場合は「r.ForceDebugViewModes」を1に)")); Single.ConsoleCommandName = TEXT("viewmode LightMapDensity"); ConsoleCommandNames.Add(Single); Single.Title = FText::FromString(TEXT("Shader Complexity")); Single.Description = FText::FromString(TEXT("シーンの各ピクセルの計算に使用しているシェーダ命令数を視覚化する\nエディターでかつStandaloneのみ有効\n(Package環境で使用したい場合は「r.ForceDebugViewModes」を1に)")); Single.ConsoleCommandName = TEXT("viewmode shadercomplexity"); ConsoleCommandNames.Add(Single); Single.Title = FText::FromString(TEXT("ShaderComplexity With Quadoverdraw")); Single.Description = FText::FromString(TEXT("エディターでかつStandaloneのみ有効\n(Package環境で使用したい場合は「r.ForceDebugViewModes」を1に)")); Single.ConsoleCommandName = TEXT("viewmode ShaderComplexityWithQuadOverdraw"); ConsoleCommandNames.Add(Single); Single.Title = FText::FromString(TEXT("Quadoverdraw")); Single.Description = FText::FromString(TEXT("エディターでかつStandaloneのみ有効\n(Package環境で使用したい場合は「r.ForceDebugViewModes」を1に)")); Single.ConsoleCommandName = TEXT("viewmode Quadoverdraw"); ConsoleCommandNames.Add(Single); /* EditorOnly */ Single.Title = FText::FromString(TEXT("Stationary Light Overlap")); Single.Description = FText::FromString(TEXT("Stationary Lightの重なりを視覚化する\nエディターでのみ有効")); Single.ConsoleCommandName = TEXT("show StationaryLightOverlap"); EditorOnlyConsoleCommandNames.Add(Single); Single.Title = FText::FromString(TEXT("Visualize Buffer")); Single.Description = FText::FromString(TEXT("各レンダーバッファの可視化\nエディターでのみ有効")); Single.ConsoleCommandName = TEXT("show VisualizeBuffer"); EditorOnlyConsoleCommandNames.Add(Single); /* EditorOnly */ Single.Title = FText::FromString(TEXT("Collision")); Single.Description = FText::FromString(TEXT("コリジョン(非)表示")); Single.ConsoleCommandName = TEXT("show collision"); ConsoleCommandNames.Add(Single); Single.Title = FText::FromString(TEXT("Collision Pawn")); Single.Description = FText::FromString(TEXT("")); Single.ConsoleCommandName = TEXT("show CollisionPawn"); ConsoleCommandNames.Add(Single); Single.Title = FText::FromString(TEXT("Collision Visibility")); Single.Description = FText::FromString(TEXT("")); Single.ConsoleCommandName = TEXT("show CollisionVisibility"); ConsoleCommandNames.Add(Single); Single.Title = FText::FromString(TEXT("Bounds")); Single.Description = FText::FromString(TEXT("各Actorのバウンディングボックスの表示")); Single.ConsoleCommandName = TEXT("show bounds"); ConsoleCommandNames.Add(Single); Single.Title = FText::FromString(TEXT("Shadow Frustums")); Single.Description = FText::FromString(TEXT("動的な影を生成しているフラスタムを表示")); Single.ConsoleCommandName = TEXT("show ShadowFrustums"); ConsoleCommandNames.Add(Single); Single.Title = FText::FromString(TEXT("Dynamic Shadows")); Single.Description = FText::FromString(TEXT("全ての動的シャドウを切り替え (シャドウマップ レンダリングとシャドウ フィルタリング / プロジェクション)")); Single.ConsoleCommandName = TEXT("show DynamicShadows"); ConsoleCommandNames.Add(Single); Single.Title = FText::FromString(TEXT("Volumes")); Single.Description = FText::FromString(TEXT("AVolume継承アクターのボリュームを(非)表示")); Single.ConsoleCommandName = TEXT("show Volumes"); ConsoleCommandNames.Add(Single); Single.Title = FText::FromString(TEXT("Navigation")); Single.Description = FText::FromString(TEXT("ナビメッシュの(非)表示")); Single.ConsoleCommandName = TEXT("show Navigation"); ConsoleCommandNames.Add(Single); Single.Title = FText::FromString(TEXT("Deferred Lighting")); Single.Description = FText::FromString(TEXT("すべてのディファード ライティング パスを切り替え")); Single.ConsoleCommandName = TEXT("show DeferredLighting"); ConsoleCommandNames.Add(Single); Single.Title = FText::FromString(TEXT("DirectionalLights")); Single.Description = FText::FromString(TEXT("ディレクショナルライトの表示切り替え")); Single.ConsoleCommandName = TEXT("show DirectionalLights"); ConsoleCommandNames.Add(Single); Single.Title = FText::FromString(TEXT("PointLights")); Single.Description = FText::FromString(TEXT("ポイントライトの表示切り替え")); Single.ConsoleCommandName = TEXT("show PointLights"); ConsoleCommandNames.Add(Single); Single.Title = FText::FromString(TEXT("SpotLights")); Single.Description = FText::FromString(TEXT("スポットライトの表示切り替え")); Single.ConsoleCommandName = TEXT("show SpotLights"); ConsoleCommandNames.Add(Single); Single.Title = FText::FromString(TEXT("Rendering")); Single.Description = FText::FromString(TEXT("")); Single.ConsoleCommandName = TEXT("show Rendering"); ConsoleCommandNames.Add(Single); Single.Title = FText::FromString(TEXT("SkyLighting")); Single.Description = FText::FromString(TEXT("")); Single.ConsoleCommandName = TEXT("show SkyLighting"); ConsoleCommandNames.Add(Single); Single.Title = FText::FromString(TEXT("Decals")); Single.Description = FText::FromString(TEXT("デカールの表示切り替え")); Single.ConsoleCommandName = TEXT("show Decals"); ConsoleCommandNames.Add(Single); Single.Title = FText::FromString(TEXT("Post Processing")); Single.Description = FText::FromString(TEXT("")); Single.ConsoleCommandName = TEXT("show PostProcessing"); ConsoleCommandNames.Add(Single); Single.Title = FText::FromString(TEXT("Particles")); Single.Description = FText::FromString(TEXT("")); Single.ConsoleCommandName = TEXT("show Particles"); ConsoleCommandNames.Add(Single); Single.Title = FText::FromString(TEXT("Specular")); Single.Description = FText::FromString(TEXT("")); Single.ConsoleCommandName = TEXT("show Specular"); ConsoleCommandNames.Add(Single); Single.Title = FText::FromString(TEXT("Translucency")); Single.Description = FText::FromString(TEXT("")); Single.ConsoleCommandName = TEXT("show Translucency"); ConsoleCommandNames.Add(Single); Single.Title = FText::FromString(TEXT("Diffuse")); Single.Description = FText::FromString(TEXT("")); Single.ConsoleCommandName = TEXT("show Diffuse"); ConsoleCommandNames.Add(Single); Single.Title = FText::FromString(TEXT("ReflectionEnvironment")); Single.Description = FText::FromString(TEXT("")); Single.ConsoleCommandName = TEXT("show ReflectionEnvironment"); ConsoleCommandNames.Add(Single); Single.Title = FText::FromString(TEXT("Refraction")); Single.Description = FText::FromString(TEXT("")); Single.ConsoleCommandName = TEXT("show Refraction"); ConsoleCommandNames.Add(Single); Single.Title = FText::FromString(TEXT("AmbientOcclusion")); Single.Description = FText::FromString(TEXT("")); Single.ConsoleCommandName = TEXT("show AmbientOcclusion"); ConsoleCommandNames.Add(Single); Single.Title = FText::FromString(TEXT("GlobalIllumination")); Single.Description = FText::FromString(TEXT("ベイクされた、動的な間接ライティングを切り替え")); Single.ConsoleCommandName = TEXT("show GlobalIllumination"); ConsoleCommandNames.Add(Single); Single.Title = FText::FromString(TEXT("LightFunctions")); Single.Description = FText::FromString(TEXT("ライト関数のレンダリングを切り替え")); Single.ConsoleCommandName = TEXT("show LightFunctions"); ConsoleCommandNames.Add(Single); Single.Title = FText::FromString(TEXT("DepthOfField")); Single.Description = FText::FromString(TEXT("")); Single.ConsoleCommandName = TEXT("show DepthOfField"); ConsoleCommandNames.Add(Single); Single.Title = FText::FromString(TEXT("AntiAliasing")); Single.Description = FText::FromString(TEXT("")); Single.ConsoleCommandName = TEXT("show AntiAliasing"); ConsoleCommandNames.Add(Single); Single.Title = FText::FromString(TEXT("Bloom")); Single.Description = FText::FromString(TEXT("")); Single.ConsoleCommandName = TEXT("show Bloom"); ConsoleCommandNames.Add(Single); Single.Title = FText::FromString(TEXT("StaticMeshes")); Single.Description = FText::FromString(TEXT("")); Single.ConsoleCommandName = TEXT("show StaticMeshes"); ConsoleCommandNames.Add(Single); Single.Title = FText::FromString(TEXT("SkeletalMeshes")); Single.Description = FText::FromString(TEXT("")); Single.ConsoleCommandName = TEXT("show SkeletalMeshes"); ConsoleCommandNames.Add(Single); Single.Title = FText::FromString(TEXT("Landscape")); Single.Description = FText::FromString(TEXT("")); Single.ConsoleCommandName = TEXT("show Landscape"); ConsoleCommandNames.Add(Single); Single.Title = FText::FromString(TEXT("Tessellation")); Single.Description = FText::FromString(TEXT("テッセレーションを切り替え (テッセレーション シェーダーは実行されたまま)")); Single.ConsoleCommandName = TEXT("show Tessellation"); ConsoleCommandNames.Add(Single); Single.Title = FText::FromString(TEXT("VisualizeVolumetricLightmap")); Single.Description = FText::FromString(TEXT("ボリュームライトマップの可視化")); Single.ConsoleCommandName = TEXT("show VisualizeVolumetricLightmap"); ConsoleCommandNames.Add(Single); Single.Title = FText::FromString(TEXT("ScreenSpaceReflections")); Single.Description = FText::FromString(TEXT("スクリーン スペースの反射を切り替え")); Single.ConsoleCommandName = TEXT("show ScreenSpaceReflections"); ConsoleCommandNames.Add(Single); Single.Title = FText::FromString(TEXT("Shader Complexity")); Single.Description = FText::FromString(TEXT("シーンの各ピクセルを計算するために使用されているシェーダ命令の数を視覚化")); Single.ConsoleCommandName = TEXT("show ShaderComplexity"); ConsoleCommandNames.Add(Single); /* EditorOnly */ Single.Title = FText::FromString(TEXT("Materials")); Single.Description = FText::FromString(TEXT("マテリアルのオンオフ\nエディターでのみ有効")); Single.ConsoleCommandName = TEXT("show Materials"); EditorOnlyConsoleCommandNames.Add(Single); /* EditorOnly */ } void UGameDebugMenuSettings::SetupCategoryScalability() { FGDMConsoleCommandNumber Number; Number.CategoryIndex = 7; /* Scalability */ Number.CommandNetType = EGDMConsoleCommandNetType::LocalOnly; Number.PreConsoleCommandName = TEXT("sg."); Number.PostConsoleCommandName = TEXT(" "); Number.DefaultValue = 0.0f; Number.Title = FText::FromString(TEXT("ViewDistanceQuality")); Number.Description = FText::FromString(TEXT("")); Number.ConsoleCommandName = TEXT("ViewDistanceQuality"); Number.ConsoleVariableName = TEXT("sg.ViewDistanceQuality"); Number.UIConfigInfo.Range.bUseMax = true; Number.UIConfigInfo.Range.bUseMin = true; Number.UIConfigInfo.Range.MinValue = 0.0f; Number.UIConfigInfo.Range.MaxValue = 3.0f; Number.UIConfigInfo.DefaultChangeAmount = 1.0f; Number.UIConfigInfo.MaxChangeAmount = 1.0f; ConsoleCommandNumbers.Add(Number); Number.Title = FText::FromString(TEXT("AntiAliasingQuality")); Number.Description = FText::FromString(TEXT("")); Number.ConsoleCommandName = TEXT("AntiAliasingQuality"); Number.ConsoleVariableName = TEXT("sg.AntiAliasingQuality"); Number.UIConfigInfo.Range.bUseMax = true; Number.UIConfigInfo.Range.bUseMin = true; Number.UIConfigInfo.Range.MinValue = 0.0f; Number.UIConfigInfo.Range.MaxValue = 6.0f; Number.UIConfigInfo.DefaultChangeAmount = 1.0f; Number.UIConfigInfo.MaxChangeAmount = 1.0f; ConsoleCommandNumbers.Add(Number); Number.Title = FText::FromString(TEXT("PostProcessQuality")); Number.Description = FText::FromString(TEXT("")); Number.ConsoleCommandName = TEXT("PostProcessQuality"); Number.ConsoleVariableName = TEXT("sg.PostProcessQuality"); Number.UIConfigInfo.Range.bUseMax = true; Number.UIConfigInfo.Range.bUseMin = true; Number.UIConfigInfo.Range.MinValue = 0.0f; Number.UIConfigInfo.Range.MaxValue = 3.0f; Number.UIConfigInfo.DefaultChangeAmount = 1.0f; Number.UIConfigInfo.MaxChangeAmount = 1.0f; ConsoleCommandNumbers.Add(Number); Number.Title = FText::FromString(TEXT("ShadowQuality")); Number.Description = FText::FromString(TEXT("")); Number.ConsoleCommandName = TEXT("ShadowQuality"); Number.ConsoleVariableName = TEXT("sg.ShadowQuality"); Number.UIConfigInfo.Range.bUseMax = true; Number.UIConfigInfo.Range.bUseMin = true; Number.UIConfigInfo.Range.MinValue = 0.0f; Number.UIConfigInfo.Range.MaxValue = 3.0f; Number.UIConfigInfo.DefaultChangeAmount = 1.0f; Number.UIConfigInfo.MaxChangeAmount = 1.0f; ConsoleCommandNumbers.Add(Number); Number.Title = FText::FromString(TEXT("TextureQuality")); Number.Description = FText::FromString(TEXT("")); Number.ConsoleCommandName = TEXT("TextureQuality"); Number.ConsoleVariableName = TEXT("sg.TextureQuality"); Number.UIConfigInfo.Range.bUseMax = true; Number.UIConfigInfo.Range.bUseMin = true; Number.UIConfigInfo.Range.MinValue = 0.0f; Number.UIConfigInfo.Range.MaxValue = 3.0f; Number.UIConfigInfo.DefaultChangeAmount = 1.0f; Number.UIConfigInfo.MaxChangeAmount = 1.0f; ConsoleCommandNumbers.Add(Number); Number.Title = FText::FromString(TEXT("EffectsQuality")); Number.Description = FText::FromString(TEXT("")); Number.ConsoleCommandName = TEXT("EffectsQuality"); Number.ConsoleVariableName = TEXT("sg.EffectsQuality"); Number.UIConfigInfo.Range.bUseMax = true; Number.UIConfigInfo.Range.bUseMin = true; Number.UIConfigInfo.Range.MinValue = 0.0f; Number.UIConfigInfo.Range.MaxValue = 3.0f; Number.UIConfigInfo.DefaultChangeAmount = 1.0f; Number.UIConfigInfo.MaxChangeAmount = 1.0f; ConsoleCommandNumbers.Add(Number); Number.Title = FText::FromString(TEXT("FoliageQuality")); Number.Description = FText::FromString(TEXT("")); Number.ConsoleCommandName = TEXT("FoliageQuality"); Number.ConsoleVariableName = TEXT("sg.FoliageQuality"); Number.UIConfigInfo.Range.bUseMax = true; Number.UIConfigInfo.Range.bUseMin = true; Number.UIConfigInfo.Range.MinValue = 0.0f; Number.UIConfigInfo.Range.MaxValue = 3.0f; Number.UIConfigInfo.DefaultChangeAmount = 1.0f; Number.UIConfigInfo.MaxChangeAmount = 1.0f; ConsoleCommandNumbers.Add(Number); Number.Title = FText::FromString(TEXT("ShadingQuality")); Number.Description = FText::FromString(TEXT("")); Number.ConsoleCommandName = TEXT("ShadingQuality"); Number.ConsoleVariableName = TEXT("sg.ShadingQuality"); Number.UIConfigInfo.Range.bUseMax = true; Number.UIConfigInfo.Range.bUseMin = true; Number.UIConfigInfo.Range.MinValue = 0.0f; Number.UIConfigInfo.Range.MaxValue = 3.0f; Number.UIConfigInfo.DefaultChangeAmount = 1.0f; Number.UIConfigInfo.MaxChangeAmount = 1.0f; ConsoleCommandNumbers.Add(Number); Number.PreConsoleCommandName = TEXT("r."); Number.PostConsoleCommandName = TEXT(" "); Number.Title = FText::FromString(TEXT("ScreenPercentage")); Number.Description = FText::FromString(TEXT("")); Number.ConsoleCommandName = TEXT("ScreenPercentage"); Number.ConsoleVariableName = TEXT("r.ScreenPercentage"); Number.UIConfigInfo.Range.bUseMax = true; Number.UIConfigInfo.Range.bUseMin = true; Number.UIConfigInfo.Range.MinValue = 10.0f; Number.UIConfigInfo.Range.MaxValue = 200.0f; Number.UIConfigInfo.DefaultChangeAmount = 1.0f; Number.UIConfigInfo.MaxChangeAmount = 5.0f; ConsoleCommandNumbers.Add(Number); Number.Title = FText::FromString(TEXT("DynamicRes.OperationMode")); Number.Description = FText::FromString(TEXT("動的解像度の使用方法の切り替え\n 0 = 無効\n 1 = GameUserSettingsの設定に基づいて有効化\n2 = GameUserSettingsを考慮せず有効化")); Number.ConsoleCommandName = TEXT("DynamicRes.OperationMode"); Number.ConsoleVariableName = TEXT("r.DynamicRes.OperationMode"); Number.UIConfigInfo.Range.bUseMax = true; Number.UIConfigInfo.Range.bUseMin = true; Number.UIConfigInfo.Range.MinValue = 0.0f; Number.UIConfigInfo.Range.MaxValue = 2.0f; Number.UIConfigInfo.DefaultChangeAmount = 1.0f; Number.UIConfigInfo.MaxChangeAmount = 1.0f; ConsoleCommandNumbers.Add(Number); Number.Title = FText::FromString(TEXT("MaterialQualityLevel")); Number.Description = FText::FromString(TEXT("マテリアル品質レベルの変更\n0 = Low: 1 = High: 2 = Medium")); Number.ConsoleCommandName = TEXT("MaterialQualityLevel"); Number.ConsoleVariableName = TEXT("r.MaterialQualityLevel"); Number.UIConfigInfo.Range.bUseMax = true; Number.UIConfigInfo.Range.bUseMin = true; Number.UIConfigInfo.Range.MinValue = 0.0f; Number.UIConfigInfo.Range.MaxValue = 2.0f; Number.UIConfigInfo.DefaultChangeAmount = 1.0f; Number.UIConfigInfo.MaxChangeAmount = 1.0f; ConsoleCommandNumbers.Add(Number); Number.PreConsoleCommandName = TEXT("r.DOF."); Number.Title = FText::FromString(TEXT("DOF - MaxBackgroundRadius")); Number.Description = FText::FromString(TEXT("水平スクリーン空間のバックグラウンド ブラー半径の最大サイズ")); Number.ConsoleCommandName = TEXT("Kernel.MaxBackgroundRadius"); Number.ConsoleVariableName = TEXT("r.DOF.Kernel.MaxBackgroundRadius"); Number.UIConfigInfo.Range.bUseMax = true; Number.UIConfigInfo.Range.bUseMin = true; Number.UIConfigInfo.Range.MinValue = 0.0f; Number.UIConfigInfo.Range.MaxValue = 1.0f; Number.UIConfigInfo.DefaultChangeAmount = 0.001f; Number.UIConfigInfo.MaxChangeAmount = 0.005f; ConsoleCommandNumbers.Add(Number); Number.Title = FText::FromString(TEXT("DOF - MaxForegroundRadius")); Number.Description = FText::FromString(TEXT("水平スクリーン空間のフォアグラウンド ブラー半径の最大サイズ")); Number.ConsoleCommandName = TEXT("Kernel.MaxForegroundRadius"); Number.ConsoleVariableName = TEXT("r.DOF.Kernel.MaxForegroundRadius"); Number.UIConfigInfo.Range.bUseMax = true; Number.UIConfigInfo.Range.bUseMin = true; Number.UIConfigInfo.Range.MinValue = 0.0f; Number.UIConfigInfo.Range.MaxValue = 1.0f; Number.UIConfigInfo.DefaultChangeAmount = 0.001f; Number.UIConfigInfo.MaxChangeAmount = 0.005f; ConsoleCommandNumbers.Add(Number); Number.Title = FText::FromString(TEXT("DOF - MaxSpriteRatio")); Number.Description = FText::FromString(TEXT("スプライトとしての散乱ピクセル クワッドの最大比率\nDOF の散乱の上限をコントロールするために便利\n1では100%のピクセルクワッドを散乱可能に")); Number.ConsoleCommandName = TEXT("Scatter.MaxSpriteRatio"); Number.ConsoleVariableName = TEXT("r.DOF.Scatter.MaxSpriteRatio"); Number.UIConfigInfo.Range.bUseMax = true; Number.UIConfigInfo.Range.bUseMin = true; Number.UIConfigInfo.Range.MinValue = 0.0f; Number.UIConfigInfo.Range.MaxValue = 1.0f; Number.UIConfigInfo.DefaultChangeAmount = 0.01f; Number.UIConfigInfo.MaxChangeAmount = 0.05f; ConsoleCommandNumbers.Add(Number); FGDMConsoleCommandPair Pair; Pair.CategoryIndex = 7; /* Scalability */ Pair.CommandNetType = EGDMConsoleCommandNetType::LocalOnly; Pair.Title = FText::FromString(TEXT("Toggle VSync")); Pair.Description = FText::FromString(TEXT("垂直同期の切り替え")); Pair.ClickedEvent = EGDMConsoleCommandClickedEvent::Non; Pair.FirstConsoleCommandName = TEXT("r.VSync 0"); Pair.SecondConsoleCommandName = TEXT("r.VSync 1"); ConsoleCommandPairs.Add(Pair); } void UGameDebugMenuSettings::SetupCategoryFreeze() { FGDMConsoleCommandSingle Single; Single.CategoryIndex = 8; /* Freeze */ Single.CommandNetType = EGDMConsoleCommandNetType::LocalOnly; Single.ClickedEvent = EGDMConsoleCommandClickedEvent::Non; Single.Title = FText::FromString(TEXT("Freeze Rendering")); Single.Description = FText::FromString(TEXT("レベル内にあるオクルードされたアクタ\nおよび表示されているアクタの現在のレンダリングの状態を一時停止 / 解除する")); Single.ConsoleCommandName = TEXT("FreezeRendering"); ConsoleCommandNames.Add(Single); Single.Title = FText::FromString(TEXT("FreezeParticleSimulation")); Single.Description = FText::FromString(TEXT("レベル内のすべての CPU スプライト パーティクル シミュレーション\nの一時停止 / 解除する")); Single.ConsoleCommandName = TEXT("FX.FreezeParticleSimulation"); ConsoleCommandNames.Add(Single); Single.Title = FText::FromString(TEXT("FreezeGPUSimulation")); Single.Description = FText::FromString(TEXT("レベル内のすべての GPU スプライト パーティクル シミュレーション\nの一時停止 / 解除する")); Single.ConsoleCommandName = TEXT("FX.FreezeGPUSimulation"); ConsoleCommandNames.Add(Single); FGDMConsoleCommandPair Pair; Pair.CategoryIndex = 8; /* Freeze */ Pair.CommandNetType = EGDMConsoleCommandNetType::LocalOnly; Pair.Title = FText::FromString(TEXT("Foliage Freeze / Unfreeze")); Pair.Description = FText::FromString(TEXT("レベル内でオクルードおよび表示された\nペイントされたフォリッジ クラスタの現在のレンダリングの状態を一時停止 / 解除する")); Pair.ClickedEvent = EGDMConsoleCommandClickedEvent::Non; Pair.FirstConsoleCommandName = TEXT("Foliage.Freeze"); Pair.SecondConsoleCommandName = TEXT("Foliage.Unfreeze"); ConsoleCommandPairs.Add(Pair); } void UGameDebugMenuSettings::SetupCategoryDumpLogs() { FGDMConsoleCommandSingle Single; Single.CategoryIndex = 9; /* Dump Logs */ Single.CommandNetType = EGDMConsoleCommandNetType::LocalOnly; Single.Title = FText::FromString(TEXT("Obj List")); Single.Description = FText::FromString(TEXT("OutputLogウィンドウに各オブジェクトの数と使用メモリ量を出力する")); Single.ConsoleCommandName = TEXT("Obj List"); ConsoleCommandNames.Add(Single); Single.Title = FText::FromString(TEXT("Obj List Forget")); Single.Description = FText::FromString(TEXT("Obj Listで取得したObjectを記憶し\n これ以降のObj Listでは差分が表示されるようになる")); Single.ConsoleCommandName = TEXT("Obj List Forget"); ConsoleCommandNames.Add(Single); Single.Title = FText::FromString(TEXT("Obj List Remember")); Single.Description = FText::FromString(TEXT("Obj List Forget で記憶したオブジェクトをリセットする")); Single.ConsoleCommandName = TEXT("Obj List Remember"); ConsoleCommandNames.Add(Single); Single.Title = FText::FromString(TEXT("MemReport")); Single.Description = FText::FromString(TEXT("YourGame/Saved/Profiling/MemReports以下にメモリ使用ログを出力する")); Single.ConsoleCommandName = TEXT("memreport"); ConsoleCommandNames.Add(Single); Single.Title = FText::FromString(TEXT("MemReport Full")); Single.Description = FText::FromString(TEXT("MemReportのより詳細にログを出力します")); Single.ConsoleCommandName = TEXT("memreport -full"); ConsoleCommandNames.Add(Single); Single.Title = FText::FromString(TEXT("Dump Ticks")); Single.Description = FText::FromString(TEXT("Level上に存在するActorやComponentのTickが有効なものをリストアップしログ表示する")); Single.ConsoleCommandName = TEXT("dumpticks Enable"); ConsoleCommandNames.Add(Single); Single.Title = FText::FromString(TEXT("Loadtimes Dumpreport")); Single.Description = FText::FromString(TEXT("ロードされたファイルのロード時間を降順でログ出力する\n(loadtimes.resetでタイミング調整可能)")); Single.ConsoleCommandName = TEXT("loadtimes.dumpreport"); ConsoleCommandNames.Add(Single); Single.Title = FText::FromString(TEXT("Dump Hitches")); Single.Description = FText::FromString(TEXT("t.HitchFrameTimeThreshold に基づいて処理落ちが検出されるたびに\nログへ書き込まれる")); Single.ConsoleCommandName = TEXT("stat DumpHitches"); ConsoleCommandNames.Add(Single); Single.Title = FText::FromString(TEXT("Log Garbage")); Single.Description = FText::FromString(TEXT("GCが起きたときの検索、削除コストをログに表示する")); Single.ConsoleCommandName = TEXT("log LogGarbage log"); ConsoleCommandNames.Add(Single); Single.Title = FText::FromString(TEXT("Log List")); Single.Description = FText::FromString(TEXT("各ログカテゴリの一覧と、各カテゴリの現在の設定を確認できる")); Single.ConsoleCommandName = TEXT("log list"); ConsoleCommandNames.Add(Single); FGDMConsoleCommandNumber Number; Number.CategoryIndex = 9; /* Dump Logs */ Number.CommandNetType = EGDMConsoleCommandNetType::LocalOnly; Number.Title = FText::FromString(TEXT("Hitch Frame Time Threshold")); Number.Description = FText::FromString(TEXT("DumpHitches実行時ログに出力するかどうかのしきい値(ミリ秒)\n この値よりも処理に時間がかかるものがログに出力されるようになる")); Number.PreConsoleCommandName = TEXT("t."); Number.ConsoleCommandName = TEXT("HitchFrameTimeThreshold"); Number.PostConsoleCommandName = TEXT(" "); Number.ConsoleVariableName = TEXT("t.HitchFrameTimeThreshold"); Number.UIConfigInfo.Range.bUseMin = true; Number.UIConfigInfo.Range.bUseMax = false; Number.UIConfigInfo.Range.MinValue = 10.0f; Number.UIConfigInfo.DefaultChangeAmount = 5.0f; Number.UIConfigInfo.MaxChangeAmount = 10.0f; Number.DefaultValue = FEnginePerformanceTargets::GetHitchFrameTimeThresholdMS(); ConsoleCommandNumbers.Add(Number); } void UGameDebugMenuSettings::SetupCategoryNetwork() { FGDMConsoleCommandNumber Number; Number.CategoryIndex = 10; /* Network */ Number.ConsoleVariableName = TEXT(""); /* NetEmulation系は FConsoleCommandWithWorldAndArgsDelegate で定義されてるため * IConsoleObjectから値が取れない。。。 */ Number.Title = FText::FromString(TEXT("PktLag")); Number.Description = FText::FromString(TEXT("パケット送信を遅らせる(ミリ秒)")); Number.PreConsoleCommandName = TEXT("NetEmulation."); Number.ConsoleCommandName = TEXT("PktLag"); Number.PostConsoleCommandName = TEXT(" "); Number.DefaultValue = 0.0f; Number.CommandNetType = EGDMConsoleCommandNetType::ServerAll; Number.UIConfigInfo.Range.bUseMax = false; Number.UIConfigInfo.Range.bUseMin = true; Number.UIConfigInfo.Range.MinValue = 0.0f; Number.UIConfigInfo.Range.MaxValue = 1.0f; Number.UIConfigInfo.DefaultChangeAmount = 10; Number.UIConfigInfo.MaxChangeAmount = 100; ConsoleCommandNumbers.Add(Number); Number.Title = FText::FromString(TEXT("PktLagVariance")); Number.Description = FText::FromString(TEXT("パケット送信を指定範囲内でランダムに遅らせる(±ミリ秒)")); Number.PreConsoleCommandName = TEXT("NetEmulation."); Number.ConsoleCommandName = TEXT("PktLagVariance"); Number.PostConsoleCommandName = TEXT(" "); Number.DefaultValue = 0.0f; Number.CommandNetType = EGDMConsoleCommandNetType::ServerAll; Number.UIConfigInfo.Range.bUseMax = false; Number.UIConfigInfo.Range.bUseMin = true; Number.UIConfigInfo.Range.MinValue = 0.0f; Number.UIConfigInfo.Range.MaxValue = 1.0f; Number.UIConfigInfo.DefaultChangeAmount = 10; Number.UIConfigInfo.MaxChangeAmount = 100; ConsoleCommandNumbers.Add(Number); Number.Title = FText::FromString(TEXT("PktLoss")); Number.Description = FText::FromString(TEXT("一定の確率でパケットを送信しないようにする(0~100%)")); Number.PreConsoleCommandName = TEXT("NetEmulation."); Number.ConsoleCommandName = TEXT("PktLoss"); Number.PostConsoleCommandName = TEXT(" "); Number.DefaultValue = 0.0f; Number.CommandNetType = EGDMConsoleCommandNetType::ServerAll; Number.UIConfigInfo.Range.bUseMax = true; Number.UIConfigInfo.Range.bUseMin = true; Number.UIConfigInfo.Range.MinValue = 0.0f; Number.UIConfigInfo.Range.MaxValue = 100.0f; Number.UIConfigInfo.DefaultChangeAmount = 1; Number.UIConfigInfo.MaxChangeAmount = 10; ConsoleCommandNumbers.Add(Number); Number.Title = FText::FromString(TEXT("PktOrder")); Number.Description = FText::FromString(TEXT("パケットをバッファリングして送信順序をランダムにする(1=有効、0=無効)")); Number.PreConsoleCommandName = TEXT("NetEmulation."); Number.ConsoleCommandName = TEXT("PktOrder"); Number.PostConsoleCommandName = TEXT(" "); Number.DefaultValue = 0.0f; Number.CommandNetType = EGDMConsoleCommandNetType::ServerAll; Number.UIConfigInfo.Range.bUseMax = true; Number.UIConfigInfo.Range.bUseMin = true; Number.UIConfigInfo.Range.MinValue = 0.0f; Number.UIConfigInfo.Range.MaxValue = 1.0f; Number.UIConfigInfo.DefaultChangeAmount = 1; Number.UIConfigInfo.MaxChangeAmount = 1; ConsoleCommandNumbers.Add(Number); Number.Title = FText::FromString(TEXT("PktDup")); Number.Description = FText::FromString(TEXT("パケット送信時に指定確率で重複送信する(0~100%)")); Number.PreConsoleCommandName = TEXT("NetEmulation."); Number.ConsoleCommandName = TEXT("PktDup"); Number.PostConsoleCommandName = TEXT(" "); Number.DefaultValue = 0.0f; Number.CommandNetType = EGDMConsoleCommandNetType::ServerAll; Number.UIConfigInfo.Range.bUseMax = true; Number.UIConfigInfo.Range.bUseMin = true; Number.UIConfigInfo.Range.MinValue = 0.0f; Number.UIConfigInfo.Range.MaxValue = 100.0f; Number.UIConfigInfo.DefaultChangeAmount = 1; Number.UIConfigInfo.MaxChangeAmount = 10; ConsoleCommandNumbers.Add(Number); Number.Title = FText::FromString(TEXT("RPC Debug")); Number.Description = FText::FromString(TEXT("すべてのRPC Bunchをログ表示(1=有効、0=無効)")); Number.PreConsoleCommandName = TEXT("net."); Number.ConsoleCommandName = TEXT("RPC"); Number.PostConsoleCommandName = TEXT(".Debug "); Number.ConsoleVariableName = TEXT("net.RPC.Debug"); Number.DefaultValue = 0.0f; Number.CommandNetType = EGDMConsoleCommandNetType::LocalOnly; Number.UIConfigInfo.Range.bUseMax = true; Number.UIConfigInfo.Range.bUseMin = true; Number.UIConfigInfo.Range.MinValue = 0.0f; Number.UIConfigInfo.Range.MaxValue = 1.0f; Number.UIConfigInfo.DefaultChangeAmount = 1; Number.UIConfigInfo.MaxChangeAmount = 1; ConsoleCommandNumbers.Add(Number); Number.Title = FText::FromString(TEXT("Reliable Debug")); Number.Description = FText::FromString(TEXT("すべてのReliable Bunchのログ表示(0=出力なし、1=送信時のみ出力、2=更新時に出力)")); Number.PreConsoleCommandName = TEXT("net."); Number.ConsoleCommandName = TEXT("Reliable"); Number.PostConsoleCommandName = TEXT(".Debug "); Number.ConsoleVariableName = TEXT("net.Reliable.Debug"); Number.DefaultValue = 0.0f; Number.CommandNetType = EGDMConsoleCommandNetType::LocalOnly; Number.UIConfigInfo.Range.bUseMax = true; Number.UIConfigInfo.Range.bUseMin = true; Number.UIConfigInfo.Range.MinValue = 0.0f; Number.UIConfigInfo.Range.MaxValue = 2.0f; Number.UIConfigInfo.DefaultChangeAmount = 1; Number.UIConfigInfo.MaxChangeAmount = 1; ConsoleCommandNumbers.Add(Number); Number.Title = FText::FromString(TEXT("NetShowCorrections")); Number.Description = FText::FromString(TEXT("クライアント (またはサーバーへ送信) がネットワーク修正を受け取った時期を確認できるようにする(1=有効、0=無効)")); Number.PreConsoleCommandName = TEXT("p."); Number.ConsoleCommandName = TEXT("NetShowCorrections"); Number.PostConsoleCommandName = TEXT(" "); Number.ConsoleVariableName = TEXT("p.NetShowCorrections"); Number.DefaultValue = 0.0f; Number.CommandNetType = EGDMConsoleCommandNetType::LocalOnly; Number.UIConfigInfo.Range.bUseMax = true; Number.UIConfigInfo.Range.bUseMin = true; Number.UIConfigInfo.Range.MinValue = 0.0f; Number.UIConfigInfo.Range.MaxValue = 1.0f; Number.UIConfigInfo.DefaultChangeAmount = 1; Number.UIConfigInfo.MaxChangeAmount = 1; ConsoleCommandNumbers.Add(Number); Number.Title = FText::FromString(TEXT("Debug Draw")); Number.Description = FText::FromString(TEXT("ネットワークのdormancyとrelevancyの情報を表示(1=有効、0=無効)")); Number.PreConsoleCommandName = TEXT("net."); Number.ConsoleCommandName = TEXT("DebugDraw"); Number.PostConsoleCommandName = TEXT(" "); Number.ConsoleVariableName = TEXT("net.DebugDraw"); Number.DefaultValue = 0.0f; Number.CommandNetType = EGDMConsoleCommandNetType::LocalOnly; Number.UIConfigInfo.Range.bUseMax = true; Number.UIConfigInfo.Range.bUseMin = true; Number.UIConfigInfo.Range.MinValue = 0.0f; Number.UIConfigInfo.Range.MaxValue = 1.0f; Number.UIConfigInfo.DefaultChangeAmount = 1; Number.UIConfigInfo.MaxChangeAmount = 1; ConsoleCommandNumbers.Add(Number); Number.Title = FText::FromString(TEXT("Context Debug")); Number.Description = FText::FromString(TEXT("Replication情報にデバッグ用文字列を設定してログに詳細な情報を表示(1=有効、0=無効)")); Number.PreConsoleCommandName = TEXT("net."); Number.ConsoleCommandName = TEXT("ContextDebug"); Number.PostConsoleCommandName = TEXT(" "); Number.ConsoleVariableName = TEXT("net.ContextDebug"); Number.DefaultValue = 0.0f; Number.CommandNetType = EGDMConsoleCommandNetType::LocalOnly; Number.UIConfigInfo.Range.bUseMax = true; Number.UIConfigInfo.Range.bUseMin = true; Number.UIConfigInfo.Range.MinValue = 0.0f; Number.UIConfigInfo.Range.MaxValue = 1.0f; Number.UIConfigInfo.DefaultChangeAmount = 1; Number.UIConfigInfo.MaxChangeAmount = 1; ConsoleCommandNumbers.Add(Number); Number.Title = FText::FromString(TEXT("Dormancy Enable")); Number.Description = FText::FromString(TEXT("頻繁に更新されるアクターのCPUおよび帯域幅のオーバーヘッドを削減するネットワーク休止システムの有効化(1=有効、0=無効)")); Number.PreConsoleCommandName = TEXT("net."); Number.ConsoleCommandName = TEXT("DormancyEnable"); Number.PostConsoleCommandName = TEXT(" "); Number.ConsoleVariableName = TEXT("net.DormancyEnable"); Number.DefaultValue = 1.0f; Number.CommandNetType = EGDMConsoleCommandNetType::LocalOnly; Number.UIConfigInfo.Range.bUseMax = true; Number.UIConfigInfo.Range.bUseMin = true; Number.UIConfigInfo.Range.MinValue = 0.0f; Number.UIConfigInfo.Range.MaxValue = 1.0f; Number.UIConfigInfo.DefaultChangeAmount = 1; Number.UIConfigInfo.MaxChangeAmount = 1; ConsoleCommandNumbers.Add(Number); Number.Title = FText::FromString(TEXT("Debug Draw CullDistance")); Number.Description = FText::FromString(TEXT("localのViewから離れているActorの休止状態を描画する\nこの距離(World単位)より近いものが描画対象(net.DebugDrawが有効かつ 0 以上指定で動作)")); Number.PreConsoleCommandName = TEXT("net."); Number.ConsoleCommandName = TEXT("DebugDrawCullDistance"); Number.PostConsoleCommandName = TEXT(" "); Number.ConsoleVariableName = TEXT("net.DebugDrawCullDistance"); Number.DefaultValue = 0.0f; Number.CommandNetType = EGDMConsoleCommandNetType::LocalOnly; Number.UIConfigInfo.Range.bUseMax = false; Number.UIConfigInfo.Range.bUseMin = true; Number.UIConfigInfo.Range.MinValue = 0.0f; Number.UIConfigInfo.Range.MaxValue = 0.0f; Number.UIConfigInfo.DefaultChangeAmount = 50; Number.UIConfigInfo.MaxChangeAmount = 100; ConsoleCommandNumbers.Add(Number); FGDMConsoleCommandSingle Single; Single.CategoryIndex = 10; /* Network */ Single.CommandNetType = EGDMConsoleCommandNetType::LocalOnly; Single.Title = FText::FromString(TEXT("NET")); Single.Description = FText::FromString(TEXT("ネットワーキング システムに関する統計情報を表示")); Single.ConsoleCommandName = TEXT("stat NET"); Single.Title = FText::FromString(TEXT("Online")); Single.Description = FText::FromString(TEXT("オンライン システムのカウンターを表示")); Single.ConsoleCommandName = TEXT("stat Online"); Single.Title = FText::FromString(TEXT("Dump Relevant Actors")); Single.Description = FText::FromString(TEXT("次回ネットワーク更新時に関連するActorの情報を出力")); Single.ConsoleCommandName = TEXT("net.DumpRelevantActors"); Single.Title = FText::FromString(TEXT("List Actor Channels")); Single.Description = FText::FromString(TEXT("ActorChannelのリスト一覧表示")); Single.ConsoleCommandName = TEXT("net.ListActorChannels"); Single.Title = FText::FromString(TEXT("List Net GUIDs")); Single.Description = FText::FromString(TEXT("NetGUIDのリスト一覧表示")); Single.ConsoleCommandName = TEXT("net.ListNetGUIDs"); Single.Title = FText::FromString(TEXT("List Net GUID Exports")); Single.Description = FText::FromString(TEXT("NetGUIDとエクスポート回数のリスト一覧表示")); Single.ConsoleCommandName = TEXT("net.ListNetGUIDExports"); } void UGameDebugMenuSettings::SetupCategorySound() { FGDMConsoleCommandSingle Single; Single.CategoryIndex = 11; /* Sounds */ Single.CommandNetType = EGDMConsoleCommandNetType::LocalOnly; FGDMConsoleCommandGroup Group; Group.CategoryIndex = 11; /* Sounds */ Group.CommandNetType = EGDMConsoleCommandNetType::LocalOnly; Group.Title = FText::FromString(TEXT("Sounds")); Group.Description = FText::FromString(TEXT("アクティブなサウンドキューとサウンドウェーブ、SoundMixeを表示")); Group.ConsoleCommandNames.Add("stat SoundMixes"); Group.ConsoleCommandNames.Add("stat SoundWaves"); Group.ConsoleCommandNames.Add("stat SoundCues"); Group.ConsoleCommandNames.Add("stat Sounds"); ConsoleCommandGroups.Add(Group); Group.ConsoleCommandNames.Empty(); } void UGameDebugMenuSettings::SetupCategoryAbilitySystem() { /* https://dev.epicgames.com/community/learning/tutorials/Y477/unreal-engine-gameplay-ability-system-debugging-tools */ FGDMConsoleCommandSingle Single; Single.CategoryIndex = 12; /* Ability System */ Single.CommandNetType = EGDMConsoleCommandNetType::LocalOnly; Single.Title = FText::FromString(TEXT("Show abilitysystem")); Single.Description = FText::FromString(TEXT("AbilitySystemのデバック表示をする")); Single.ConsoleCommandName = TEXT("showdebug abilitysystem"); ConsoleCommandNames.Add(Single); Single.Title = FText::FromString(TEXT("Close abilitysystem")); Single.Description = FText::FromString(TEXT("AbilitySystemの非表示にする")); Single.ConsoleCommandName = TEXT("showdebug"); ConsoleCommandNames.Add(Single); Single.Title = FText::FromString(TEXT("AbilitySystem NextCategory")); Single.Description = FText::FromString(TEXT("AbilitySystemのデバック表示がされてる場合表示内容(所持アビリティ、エフェクトなど)を切り替える")); Single.ConsoleCommandName = TEXT("abilitySystem.debug.nextCategory"); ConsoleCommandNames.Add(Single); Single.Title = FText::FromString(TEXT("AbilitySystem NextTarget")); Single.Description = FText::FromString(TEXT("AbilitySystemのデバック表示対象を変更する(次へ移る)")); // Single.ConsoleCommandName = TEXT("abilitysystem.debug.nexttarget"); Single.ConsoleCommandName = TEXT("NextDebugTarget"); ConsoleCommandNames.Add(Single); Single.Title = FText::FromString(TEXT("AbilitySystem PrevTarget")); Single.Description = FText::FromString(TEXT("AbilitySystemのデバック表示を対象を変更する(1つ戻る)")); // Single.ConsoleCommandName = TEXT("abilitysystem.debug.prevtarget"); Single.ConsoleCommandName = TEXT("PreviousDebugTarget"); ConsoleCommandNames.Add(Single); Single.Title = FText::FromString(TEXT("AbilitySystem DebugBasicHUD")); Single.Description = FText::FromString(TEXT("操作キャラのAttribute情報を表示")); Single.ConsoleCommandName = TEXT("AbilitySystem.DebugBasicHUD"); ConsoleCommandNames.Add(Single); Single.Title = FText::FromString(TEXT("AbilitySystem DebugAbilityTags")); Single.Description = FText::FromString(TEXT("AbilitySystemで所持するタグ情報をアクター位置にすべて表示")); Single.ConsoleCommandName = TEXT("AbilitySystem.DebugAbilityTags"); ConsoleCommandNames.Add(Single); Single.Title = FText::FromString(TEXT("AbilitySystem DebugAttribute")); Single.Description = FText::FromString(TEXT("AbilitySystemで所持するAttribute情報をアクター位置にすべて表示")); Single.ConsoleCommandName = TEXT("AbilitySystem.DebugAttribute"); ConsoleCommandNames.Add(Single); Single.Title = FText::FromString(TEXT("AbilitySystem ClearDebugAttributes")); Single.Description = FText::FromString(TEXT("AbilitySystemで所持するAttribute情報をクリアする")); Single.ConsoleCommandName = TEXT("AbilitySystem.ClearDebugAttributes"); ConsoleCommandNames.Add(Single); FGDMConsoleCommandNumber Number; Number.CategoryIndex = 12; /* Ability System */ Number.Title = FText::FromString(TEXT("AbilitySystem.DebugDrawMaxDistance")); Number.Description = FText::FromString(TEXT("アクター位置に表示するAbilitySystemのデバック情報の表示距離を設定する")); Number.PreConsoleCommandName = TEXT(""); Number.ConsoleCommandName = TEXT("AbilitySystem.DebugDrawMaxDistance"); Number.PostConsoleCommandName = TEXT(" "); Number.DefaultValue = 2048.0f; Number.ConsoleVariableName = TEXT("AbilitySystem.DebugDrawMaxDistance"); Number.CommandNetType = EGDMConsoleCommandNetType::LocalOnly; Number.UIConfigInfo.Range.bUseMax = false; Number.UIConfigInfo.Range.bUseMin = true; Number.UIConfigInfo.Range.MinValue = 10.0f; Number.UIConfigInfo.Range.MaxValue = 0.0f; Number.UIConfigInfo.DefaultChangeAmount = 10; Number.UIConfigInfo.MaxChangeAmount = 100; ConsoleCommandNumbers.Add(Number); } void UGameDebugMenuSettings::SetupCategoryOther() { FGDMConsoleCommandSingle Single; Single.CategoryIndex = 13; /* Other */ Single.CommandNetType = EGDMConsoleCommandNetType::LocalOnly; Single.ClickedEvent = EGDMConsoleCommandClickedEvent::Non; Single.Title = FText::FromString(TEXT("RestartLevel")); Single.Description = FText::FromString(TEXT("レベルの再読込み")); Single.ConsoleCommandName = TEXT("RestartLevel"); ConsoleCommandNames.Add(Single); Single.Title = FText::FromString(TEXT("ShowLog")); Single.Description = FText::FromString(TEXT("コンソールウィンドウの(非)表示する")); Single.ConsoleCommandName = TEXT("ShowLog"); ConsoleCommandNames.Add(Single); Single.Title = FText::FromString(TEXT("Try Garbage Collection")); Single.Description = FText::FromString(TEXT("ガーベジコレクションを即時実行し、定期実行タイマーをリセットする")); Single.ConsoleCommandName = TEXT("Obj trygc"); ConsoleCommandNames.Add(Single); Single.CommandNetType = EGDMConsoleCommandNetType::LocalOnly; Single.Title = FText::FromString(TEXT("Exit Game")); Single.Description = FText::FromString(TEXT("ゲームを即終了させる")); Single.ConsoleCommandName = TEXT("exit"); ConsoleCommandNames.Add(Single); FGDMConsoleCommandNumber Number; Number.CategoryIndex = 13; /* Other */ Number.Title = FText::FromString(TEXT("Change Max FPS")); Number.Description = FText::FromString(TEXT("FPSの変更")); Number.PreConsoleCommandName = TEXT("t."); Number.ConsoleCommandName = TEXT("MaxFPS"); Number.PostConsoleCommandName = TEXT(" "); Number.DefaultValue = 60.0f; Number.ConsoleVariableName = TEXT("t.MaxFPS"); Number.CommandNetType = EGDMConsoleCommandNetType::LocalOnly; Number.UIConfigInfo.Range.bUseMax = true; Number.UIConfigInfo.Range.bUseMin = true; Number.UIConfigInfo.Range.MinValue = 15.0f; Number.UIConfigInfo.Range.MaxValue = 200.0f; Number.UIConfigInfo.DefaultChangeAmount = 5; Number.UIConfigInfo.MaxChangeAmount = 10; ConsoleCommandNumbers.Add(Number); } void UGameDebugMenuSettings::SetupCategoryLogVerbosity() { /* Engine\Source\Runtime\Core\Public\Logging\LogVerbosity.hのELogVerbosity参照 */ FGDMConsoleCommandSingle Single; Single.CategoryIndex = 14; /* Log Verbosity */ Single.CommandNetType = EGDMConsoleCommandNetType::LocalOnly; /************************************************************************/ /* LogTemp */ /************************************************************************/ Single.Title = FText::FromString(TEXT("LogTemp - Display")); Single.Description = FText::FromString(TEXT("LogTempのVerbosityを「Display」に変更")); Single.ConsoleCommandName = TEXT("log LogTemp Display"); ConsoleCommandNames.Add(Single); Single.Title = FText::FromString(TEXT("LogTemp - Log")); Single.Description = FText::FromString(TEXT("LogTempのVerbosityを「Log」に変更")); Single.ConsoleCommandName = TEXT("log LogTemp Log"); ConsoleCommandNames.Add(Single); Single.Title = FText::FromString(TEXT("LogTemp - Verbose")); Single.Description = FText::FromString(TEXT("LogTempのVerbosityを「Verbose」に変更")); Single.ConsoleCommandName = TEXT("log LogTemp Verbose"); ConsoleCommandNames.Add(Single); Single.Title = FText::FromString(TEXT("LogTemp - VeryVerbose")); Single.Description = FText::FromString(TEXT("LogTempのVerbosityを「VeryVerbose」に変更")); Single.ConsoleCommandName = TEXT("log LogTemp VeryVerbose"); ConsoleCommandNames.Add(Single); /************************************************************************/ /* LogAbilitySystem */ /************************************************************************/ Single.Title = FText::FromString(TEXT("LogAbilitySystem - Display")); Single.Description = FText::FromString(TEXT("LogAbilitySystemのVerbosityを「Display」に変更")); Single.ConsoleCommandName = TEXT("log LogAbilitySystem Display"); ConsoleCommandNames.Add(Single); Single.Title = FText::FromString(TEXT("LogAbilitySystem - Log")); Single.Description = FText::FromString(TEXT("LogAbilitySystemのVerbosityを「Log」に変更")); Single.ConsoleCommandName = TEXT("log LogAbilitySystem Log"); ConsoleCommandNames.Add(Single); Single.Title = FText::FromString(TEXT("LogAbilitySystem - Verbose")); Single.Description = FText::FromString(TEXT("LogAbilitySystemのVerbosityを「Verbose」に変更")); Single.ConsoleCommandName = TEXT("log LogAbilitySystem Verbose"); ConsoleCommandNames.Add(Single); Single.Title = FText::FromString(TEXT("LogAbilitySystem - VeryVerbose")); Single.Description = FText::FromString(TEXT("LogAbilitySystemのVerbosityを「VeryVerbose」に変更")); Single.ConsoleCommandName = TEXT("log LogAbilitySystem VeryVerbose"); ConsoleCommandNames.Add(Single); } ``` `SampleProject/Plugins/GameDebugMenu/Source/GameDebugMenu/Private/GameDebugMenuTypes.cpp`: ```cpp /** * Copyright (c) 2020 akihiko moroi * * This software is released under the MIT License. * (See accompanying file LICENSE.txt or copy at http://opensource.org/licenses/MIT) */ #include "GameDebugMenuTypes.h" DEFINE_LOG_CATEGORY(LogGDM) /************************************************************************ * FGDMConsoleCommand ************************************************************************/ FGDMConsoleCommand::FGDMConsoleCommand() { CategoryIndex = 255; Title = FText::GetEmpty(); Description = FText::GetEmpty(); Type = EGDMConsoleCommandType::Non; ClickedEvent = EGDMConsoleCommandClickedEvent::Non; CommandNetType = EGDMConsoleCommandNetType::LocalOnly; } FGDMConsoleCommandSingle::FGDMConsoleCommandSingle() : Super() { ConsoleCommandName.Empty(); Type = EGDMConsoleCommandType::Single; } FString FGDMConsoleCommandSingle::BuildCommandIdentifier() const { return FString::Printf(TEXT("Single_%s"), *ConsoleCommandName.Replace(TEXT(" "), TEXT("_"))); } FGDMConsoleCommandGroup::FGDMConsoleCommandGroup() : Super() { ConsoleCommandNames.Reset(); Type = EGDMConsoleCommandType::Group; } FString FGDMConsoleCommandGroup::BuildCommandIdentifier() const { const FString Joined = FString::Join(ConsoleCommandNames, TEXT("_")); return FString::Printf(TEXT("Group_%s"), *Joined.Replace(TEXT(" "), TEXT("_"))); } FGDMConsoleCommandPair::FGDMConsoleCommandPair() : Super() { FirstConsoleCommandName.Empty(); SecondConsoleCommandName.Empty(); Type = EGDMConsoleCommandType::Pair; } FString FGDMConsoleCommandPair::BuildCommandIdentifier() const { return FString::Printf(TEXT("Pair_%s_TO_%s"), *FirstConsoleCommandName.Replace(TEXT(" "), TEXT("_")), *SecondConsoleCommandName.Replace(TEXT(" "), TEXT("_")) ); } FGDMConsoleCommandNumber::FGDMConsoleCommandNumber() : Super() { ConsoleCommandName.Empty(); PreConsoleCommandName.Empty(); PostConsoleCommandName.Empty(); Type = EGDMConsoleCommandType::Number; UIConfigInfo.Range.bUseMax = true; UIConfigInfo.Range.bUseMin = true; UIConfigInfo.Range.MinValue = 0.0f; UIConfigInfo.Range.MaxValue = 1.0f; DefaultValue = 0.0f; ConsoleVariableName.Empty(); } FString FGDMConsoleCommandNumber::BuildCommandIdentifier() const { return FString::Printf(TEXT("Number_%s_Default%.2f"), *ConsoleCommandName.Replace(TEXT(" "), TEXT("_")), DefaultValue ); } /************************************************************************ * FGDMJiraSettings ************************************************************************/ FString FGDMJiraSettings::GetAssigneeAccountIdByListIndex(int32 ListIndex) const { TArray AccountIds; AssigneeList.GenerateKeyArray(AccountIds); if(AccountIds.IsValidIndex(ListIndex)) { return AccountIds[ListIndex]; } return FString(); } FText FGDMJiraSettings::GetAssigneeTextByListIndex(int32 ListIndex) const { TArray TextList; AssigneeList.GenerateValueArray(TextList); if(TextList.IsValidIndex(ListIndex)) { return TextList[ListIndex]; } return FText(); } /************************************************************************ * FGDMMenuCategoryKey ************************************************************************/ FGDMMenuCategoryKey::FGDMMenuCategoryKey(uint8 InIndex, FString InKeyName) : Index(InIndex) , KeyName(InKeyName) { } FGDMMenuCategoryKey::FGDMMenuCategoryKey(uint8 InIndex) : Index(InIndex) , KeyName() { } FGDMMenuCategoryKey::FGDMMenuCategoryKey() : Index(0) { } bool FGDMMenuCategoryKey::operator==(FGDMMenuCategoryKey& InOther) { return InOther.Index == Index; } bool FGDMMenuCategoryKey::operator!=(FGDMMenuCategoryKey& InOther) { return !((*this) == InOther); } bool FGDMMenuCategoryKey::operator<(FGDMMenuCategoryKey& InOther) { return InOther.Index < Index; } bool FGDMMenuCategoryKey::operator>(FGDMMenuCategoryKey& InOther) { return InOther.Index > Index; } /************************************************************************ * FGDMGameplayCategoryKey ************************************************************************/ FGDMGameplayCategoryKey::FGDMGameplayCategoryKey(uint8 InKey, FString InKeyName) : Super(InKey,InKeyName) { } FGDMGameplayCategoryKey::FGDMGameplayCategoryKey(uint8 InKey) : Super(InKey) { } FGDMGameplayCategoryKey::FGDMGameplayCategoryKey() : Super() { } ``` `SampleProject/Plugins/GameDebugMenu/Source/GameDebugMenu/Private/Input/GDMDebugCameraInput.cpp`: ```cpp /** * Copyright (c) 2020 akihiko moroi * * This software is released under the MIT License. * (See accompanying file LICENSE.txt or copy at http://opensource.org/licenses/MIT) */ #include "Input/GDMDebugCameraInput.h" #include "Engine/World.h" #include "Engine/DebugCameraController.h" #include "GameDebugMenuManager.h" #include AGDMDebugCameraInput::AGDMDebugCameraInput() : Super() , DebugCameraController(nullptr) { PrimaryActorTick.bCanEverTick = false; PrimaryActorTick.bStartWithTickEnabled = false; } AGameDebugMenuManager* AGDMDebugCameraInput::GetOwnerGameDebugMenuManager() const { return Cast(GetOwner()); } ADebugCameraController* AGDMDebugCameraInput::GetDebugCameraController() const { return DebugCameraController.Get(); } void AGDMDebugCameraInput::SetDebugCameraController(ADebugCameraController* DCC) { DebugCameraController = DCC; } void AGDMDebugCameraInput::ToggleOrbitHitPoint() { UE_LOG(LogGDM, Log, TEXT("AGDMDebugCameraInput::ToggleOrbitHitPoint Call")); AGameDebugMenuManager* DebugMenuManager = GetOwnerGameDebugMenuManager(); if (!IsValid(DebugMenuManager)) { return; } if (DebugMenuManager->IsInputIgnored()) { return; } if(DebugCameraController.IsValid()) { DebugCameraController->ToggleOrbitHitPoint(); } } void AGDMDebugCameraInput::PawnTeleport() { UE_LOG(LogGDM, Log, TEXT("AGDMDebugCameraInput::PawnTeleport Call")); AGameDebugMenuManager* DebugMenuManager = GetOwnerGameDebugMenuManager(); if (!IsValid(DebugMenuManager)) { return; } if (DebugMenuManager->IsInputIgnored()) { return; } DebugMenuManager->ExecuteConsoleCommand(TEXT("Teleport"), DebugMenuManager->GetOwnerPlayerController()); } ``` `SampleProject/Plugins/GameDebugMenu/Source/GameDebugMenu/Private/Input/GDMEnhancedInputComponent.cpp`: ```cpp /** * Copyright (c) 2020 akihiko moroi * * This software is released under the MIT License. * (See accompanying file LICENSE.txt or copy at http://opensource.org/licenses/MIT) */ #include "Input/GDMEnhancedInputComponent.h" #include "GameDebugMenuFunctions.h" #include "GameDebugMenuSettings.h" UGDMEnhancedInputComponent::UGDMEnhancedInputComponent() { PrimaryComponentTick.bCanEverTick = false; PrimaryComponentTick.bStartWithTickEnabled = false; /* 最後に追加されたUI用InputComponentだけ処理したい(アンバインド処理をしなくてもいいように)のでブロック指定にする */ bBlockInput = true; /* DebugMenuはEnhancedInputを利用するので他のInputComponentより優先度は高くなるような値を指定 */ Priority = GetDefault()->WidgetInputActionPriority; } bool UGDMEnhancedInputComponent::CanProcessInputAction(const UInputAction* Action) const { AGameDebugMenuManager* Manager = UGameDebugMenuFunctions::GetGameDebugMenuManager(this); if (!IsValid(Manager)) { return false; } if (Manager->IsInputIgnored()) { return false; } return true; } FEnhancedInputActionEventBinding& UGDMEnhancedInputComponent::BindDebugMenuAction(const UInputAction* Action, ETriggerEvent TriggerEvent, UObject* Object, FName FunctionName) { return BindActionInstanceLambda( Action, TriggerEvent, [this, Object, FunctionName, Action](const FInputActionInstance& Instance) { if (CanProcessInputAction(Action) && IsValid(Object)) { if (UFunction* Func = Object->FindFunction(FunctionName)) { Object->ProcessEvent(Func, nullptr); } } }); } FEnhancedInputActionEventBinding& UGDMEnhancedInputComponent::BindDebugMenuAction(const UInputAction* Action, ETriggerEvent TriggerEvent, TFunction&& InCallback) { return BindActionInstanceLambda( Action, TriggerEvent, [this, Action, Callback = MoveTemp(InCallback)](const FInputActionInstance& Instance) { if (CanProcessInputAction(Action)) { Callback(Instance); } }); } ``` `SampleProject/Plugins/GameDebugMenu/Source/GameDebugMenu/Private/Input/GDMInputSystemComponent.cpp`: ```cpp /** * Copyright (c) 2020 akihiko moroi * * This software is released under the MIT License. * (See accompanying file LICENSE.txt or copy at http://opensource.org/licenses/MIT) */ #include "Input/GDMInputSystemComponent.h" #include #include "EnhancedInputSubsystems.h" #include "Engine/DebugCameraController.h" #include "GameFramework/CheatManager.h" #include "GameDebugMenuFunctions.h" #include "GameDebugMenuManager.h" #include "Component/GDMLocalizeStringComponent.h" #include "Input/GDMDebugCameraInput.h" #include "Input/GDMEnhancedInputComponent.h" #include "Widgets/GameDebugMenuRootWidget.h" UGDMInputSystemComponent::UGDMInputSystemComponent() : IgnoreDebugMenuInput(0) , DebugCameraInput(nullptr) , RegisteredInputGroups() , ActiveInputStacks() , CurrentInputGroupName(NAME_None) , bMenuOpen(false) , RootWidgetInputComponent(nullptr) , AddInputMappingContextWhenCreateManager() , AddInputMappingContextWhenDebugMenuIsShow() , ActorSpawnedDelegateHandle() , DebugCameraController(nullptr) , bOutputDebugLog(false) { PrimaryComponentTick.bCanEverTick = true; PrimaryComponentTick.bStartWithTickEnabled = true; PrimaryComponentTick.TickGroup = ETickingGroup::TG_PrePhysics; PrimaryComponentTick.bTickEvenWhenPaused = true; bNeverNeedsRenderUpdate = true; } void UGDMInputSystemComponent::TickComponent(float DeltaTime, enum ELevelTick TickType, FActorComponentTickFunction* ThisTickFunction) { Super::TickComponent(DeltaTime, TickType, ThisTickFunction); if (bOutputDebugLog) { FString DebugText; const AGameDebugMenuManager* Manager = GetOwnerGameDebugMenuManager(); FString GroupName; Manager->GetLocalizeStringComponent()->GetString(CurrentInputGroupName.ToString(), GroupName); DebugText += FString::Printf(TEXT("[GameDebugMenu] Input Current Group: %s\n"), *GroupName); if (const TArray>* Stack = ActiveInputStacks.Find(CurrentInputGroupName)) { for (const TWeakObjectPtr& Comp : *Stack) { if (const UInputComponent* Input = Comp.Get()) { const UObject* Outer = Input->GetOuter(); FString OuterLabel = TEXT("(null)"); if (IsValid(Outer)) { FString AssetName; Outer->GetFullName(nullptr).Split(TEXT("."), nullptr, &AssetName, ESearchCase::IgnoreCase, ESearchDir::FromEnd); OuterLabel = AssetName; } DebugText += FString::Printf(TEXT(" > %s\n"), *OuterLabel); } } } GEngine->AddOnScreenDebugMessage(-1, 0.0f, FColor::Green, DebugText); } } void UGDMInputSystemComponent::EndPlay(const EEndPlayReason::Type EndPlayReason) { Super::EndPlay(EndPlayReason); if(IsValid(DebugCameraInput)) { DebugCameraInput->K2_DestroyActor(); DebugCameraInput = nullptr; } } void UGDMInputSystemComponent::SetIgnoreInput(bool bNewInput) { IgnoreDebugMenuInput = FMath::Max(IgnoreDebugMenuInput + (bNewInput ? +1 : -1), 0); } void UGDMInputSystemComponent::ResetIgnoreInput() { IgnoreDebugMenuInput = 0; } bool UGDMInputSystemComponent::IsInputIgnored() const { return (IgnoreDebugMenuInput > 0); } void UGDMInputSystemComponent::Initialize(UGameDebugMenuManagerAsset* MenuDataAsset) { AddInputMappingContextWhenCreateManager = MenuDataAsset->AddInputMappingContextWhenCreateManager; AddInputMappingContextWhenDebugMenuIsShow = MenuDataAsset->AddInputMappingContextWhenDebugMenuIsShow; CreateDebugCameraInputClass(MenuDataAsset->DebugCameraInputClass); TArray PCs = GetPlayerControllers(); for (APlayerController* PC : PCs) { if (UEnhancedInputLocalPlayerSubsystem* Subsystem = ULocalPlayer::GetSubsystem(PC->GetLocalPlayer())) { for (const auto& Data : AddInputMappingContextWhenCreateManager) { if (!Subsystem->HasMappingContext(Data.InputMappingContext)) { Subsystem->AddMappingContext(Data.InputMappingContext, Data.Priority); } } } } if (UGameDebugMenuRootWidget* Root = GetOwnerGameDebugMenuManager()->GetDebugMenuRootWidget()) { Root->EnsureDebugMenuInputComponent(); RootWidgetInputComponent = Root->GetMyInputComponent(); } } void UGDMInputSystemComponent::RegisterInputComponent(UInputComponent* InputComponent) { /* 現在指定されたグループに対してコンポーネントを処理する */ RegisterInputComponentToGroup(InputComponent, CurrentInputGroupName); } void UGDMInputSystemComponent::UnregisterInputComponent(UInputComponent* InputComponent) { /* 現在指定されたグループに対してコンポーネントを処理する */ UnregisterInputComponentFromGroup(InputComponent, CurrentInputGroupName); } void UGDMInputSystemComponent::RegisterInputComponentToGroup(UInputComponent* InputComponent, const FName GroupName) { if ( !IsValid(InputComponent) ) { UE_LOG(LogGDM, Verbose, TEXT("RegisterInputComponent failed: Not found InputComponent")); return; } if ( InputComponent == RootWidgetInputComponent ) { UE_LOG(LogGDM, Verbose, TEXT("RegisterInputComponent failed: RootWidgetInputComponent")); return; } if (GroupName.IsNone()) { UE_LOG(LogGDM, Warning, TEXT("RegisterInputComponent failed: GroupName is None")); return; } TArray>& Group = RegisteredInputGroups.FindOrAdd(GroupName); if (Group.Contains(InputComponent)) { /* 既に登録済みであればその場合、なにもしない */ return; } Group.Add(InputComponent); if (GroupName == CurrentInputGroupName && bMenuOpen) { /* 現在のグループを指定し、メニューが開いていればコントローラーに追加 */ TArray PCs = GetPlayerControllers(); for (APlayerController* PC : PCs) { PC->PushInputComponent(InputComponent); } ActiveInputStacks.FindOrAdd(GroupName).AddUnique(InputComponent); } } void UGDMInputSystemComponent::UnregisterInputComponentFromGroup(UInputComponent* InputComponent, const FName GroupName) { if ( !IsValid(InputComponent) ) { UE_LOG(LogGDM, Verbose, TEXT("UnregisterInputComponent failed: Not found InputComponent")); return; } if (InputComponent == RootWidgetInputComponent) { UE_LOG(LogGDM, Verbose, TEXT("UnregisterInputComponent failed: RootWidgetInputComponent")); return; } if (GroupName.IsNone()) { UE_LOG(LogGDM, Warning, TEXT("UnregisterInputComponent failed: GroupName is None")); return; } if (RegisteredInputGroups.Contains(GroupName)) { RegisteredInputGroups[GroupName].Remove(InputComponent); } if (ActiveInputStacks.Contains(GroupName)) { if (ActiveInputStacks[GroupName].Remove(InputComponent) > 0) { TArray PCs = GetPlayerControllers(); for (APlayerController* PC : PCs) { PC->PopInputComponent(InputComponent); } } } } void UGDMInputSystemComponent::SwitchToInputGroup(const FName NewGroupName) { if (NewGroupName == CurrentInputGroupName) { return; } TArray PCs = GetPlayerControllers(); /* 現在のグループのInputComponentを除外 */ if (TArray>* Stack = ActiveInputStacks.Find(CurrentInputGroupName)) { for (const auto& Comp : *Stack) { if (Comp.IsValid()) { for (APlayerController* PC : PCs) { PC->PopInputComponent(Comp.Get()); } } } Stack->Reset(); } CurrentInputGroupName = NewGroupName; /* 新しいグループのInputComponentを追加 */ if (const TArray>* NewStack = RegisteredInputGroups.Find(NewGroupName)) { for (const auto& Comp : *NewStack) { if (Comp.IsValid()) { for (APlayerController* PC : PCs) { PC->PushInputComponent(Comp.Get()); } ActiveInputStacks.FindOrAdd(NewGroupName).AddUnique(Comp); } } } } void UGDMInputSystemComponent::OnOpenMenu() { if (bMenuOpen) { return; } bMenuOpen = true; const TArray>* Group = RegisteredInputGroups.Find(CurrentInputGroupName); if (Group != nullptr) { for (const auto& Comp : *Group) { if (Comp.IsValid()) { ActiveInputStacks.FindOrAdd(CurrentInputGroupName).AddUnique(Comp); } } } TArray PCs = GetPlayerControllers(); for (APlayerController* PC : PCs) { if(UEnhancedInputLocalPlayerSubsystem* Subsystem = ULocalPlayer::GetSubsystem(PC->GetLocalPlayer())) { for (const auto& Data : AddInputMappingContextWhenDebugMenuIsShow) { if (!Subsystem->HasMappingContext(Data.InputMappingContext)) { Subsystem->AddMappingContext(Data.InputMappingContext, Data.Priority); } } } if (IsValid(RootWidgetInputComponent)) { PC->PushInputComponent(RootWidgetInputComponent); } if (Group != nullptr) { for (const auto& Comp : *Group) { if (Comp.IsValid()) { PC->PushInputComponent(Comp.Get()); } } } } } void UGDMInputSystemComponent::OnCloseMenu() { if (!bMenuOpen) { return; } bMenuOpen = false; TArray>* Stack = ActiveInputStacks.Find(CurrentInputGroupName); TArray PCs = GetPlayerControllers(); for (APlayerController* PC : PCs) { if(UEnhancedInputLocalPlayerSubsystem* Subsystem = ULocalPlayer::GetSubsystem(PC->GetLocalPlayer())) { for (const auto& Data : AddInputMappingContextWhenDebugMenuIsShow) { Subsystem->RemoveMappingContext(Data.InputMappingContext); } } if (Stack != nullptr) { for (const auto& Comp : *Stack) { if (Comp.IsValid()) { PC->PopInputComponent(Comp.Get()); } } } if (IsValid(RootWidgetInputComponent)) { PC->PopInputComponent(RootWidgetInputComponent); } } if (Stack != nullptr) { Stack->Reset(); } } void UGDMInputSystemComponent::CreateDebugCameraInputClass(TSubclassOf DebugCameraInputClass) { if(!IsValid(DebugCameraInputClass)) { return; } FActorSpawnParameters SpawnInfo; SpawnInfo.Owner = GetOwner(); SpawnInfo.SpawnCollisionHandlingOverride = ESpawnActorCollisionHandlingMethod::AlwaysSpawn; DebugCameraInput = GetWorld()->SpawnActor(DebugCameraInputClass, SpawnInfo); bool bExistDCC = false; const APlayerController* PC = GetOwnerGameDebugMenuManager()->GetOwnerPlayerController(); if(IsValid(PC->CheatManager)) { if(ADebugCameraController* DCC = PC->CheatManager->DebugCameraControllerRef) { bExistDCC = true; DebugCameraInput->EnableInput(DCC); } } if(!bExistDCC) { /* ないので生成するのを待つ */ ActorSpawnedDelegateHandle = GetWorld()->AddOnActorSpawnedHandler(FOnActorSpawned::FDelegate::CreateUObject(this, &UGDMInputSystemComponent::OnActorSpawned)); } } AGameDebugMenuManager* UGDMInputSystemComponent::GetOwnerGameDebugMenuManager() const { return Cast(GetOwner()); } TArray UGDMInputSystemComponent::GetPlayerControllers() const { TArray ReturnValues; if (APlayerController* TargetPC = GetOwnerGameDebugMenuManager()->GetOwnerPlayerController()) { ReturnValues.Add(TargetPC); if (IsValid(TargetPC->CheatManager)) { if (ADebugCameraController* DCC = TargetPC->CheatManager->DebugCameraControllerRef) { /* デバックカメラ側も対象に含める */ ReturnValues.Add(DCC); } } } return ReturnValues; } void UGDMInputSystemComponent::OnActorSpawned(AActor* SpawnActor) { DebugCameraController = Cast(SpawnActor); if (!DebugCameraController.IsValid()) { return; } GetWorld()->RemoveOnActorSpawnedHandler(ActorSpawnedDelegateHandle); DebugCameraInput->SetDebugCameraController(DebugCameraController.Get()); /* DebugCamera操作中にメニュー操作できるように有効化 */ { DebugCameraInput->EnableInput(DebugCameraController.Get()); GetOwnerGameDebugMenuManager()->EnableInput(DebugCameraController.Get()); } /* 生成直後はローカルプレイヤーが取得できないので次回フレームに */ TWeakObjectPtr WeakThis = this; GetWorld()->GetTimerManager().SetTimerForNextTick(FTimerDelegate::CreateWeakLambda(this, [WeakThis] { if(WeakThis.IsValid()) { if(UEnhancedInputLocalPlayerSubsystem* Subsystem = ULocalPlayer::GetSubsystem(WeakThis->DebugCameraController->GetLocalPlayer())) { for (const auto& Data : WeakThis->AddInputMappingContextWhenCreateManager) { if (!Subsystem->HasMappingContext(Data.InputMappingContext)) { Subsystem->AddMappingContext(Data.InputMappingContext, Data.Priority); } } } } })); } ``` `SampleProject/Plugins/GameDebugMenu/Source/GameDebugMenu/Private/Input/GDMInputTriggerPulseWithDelay.cpp`: ```cpp /** * Copyright (c) 2020 akihiko moroi * * This software is released under the MIT License. * (See accompanying file LICENSE.txt or copy at http://opensource.org/licenses/MIT) */ #include "Input/GDMInputTriggerPulseWithDelay.h" #include "EnhancedPlayerInput.h" FString UGDMInputTriggerPulseWithDelay::GetDebugState() const { const float TimeSinceFirst = FMath::Max(0.f, HeldDuration - InitialDelay); const float TimeToNext = (TriggerCount == 0) ? InitialDelay - HeldDuration : RepeatInterval - FMath::Fmod(TimeSinceFirst, RepeatInterval); return FString::Printf(TEXT("Held:%.2f Triggers:%d TimeToNext:%.2f"), HeldDuration, TriggerCount, TimeToNext); } ETriggerState UGDMInputTriggerPulseWithDelay::UpdateState_Implementation(const UEnhancedPlayerInput* PlayerInput, FInputActionValue ModifiedValue, float DeltaTime) { ETriggerState State = ETriggerState::None; // Transition to Ongoing on actuation. Update the held duration. if (IsActuated(ModifiedValue)) { State = ETriggerState::Ongoing; HeldDuration = CalculateHeldDuration(PlayerInput, DeltaTime); } else { // Reset duration HeldDuration = 0.0f; } if (State == ETriggerState::None) { bWasPressed = false; bTriggeredOnStart = false; TriggerCount = 0; return ETriggerState::None; } /* 初回押下処理 */ if (!bWasPressed) { bWasPressed = true; if (bTriggerOnStart) { bTriggeredOnStart = true; ++TriggerCount; return ETriggerState::Triggered; } } /* トリガー制限チェック */ if (TriggerLimit > 0 && TriggerCount >= TriggerLimit) { return ETriggerState::None; } /* 初回トリガー(押下後 InitialDelay 経過) */ if (!bTriggeredOnStart && TriggerCount == 0 && HeldDuration >= InitialDelay) { ++TriggerCount; return ETriggerState::Triggered; } /* 2回目以降(RepeatInterval ごと) */ if (TriggerCount > 0) { const float TimeSinceFirst = HeldDuration - InitialDelay; const int32 ExpectedCount = FMath::FloorToInt(TimeSinceFirst / RepeatInterval) + 1; if (ExpectedCount > TriggerCount) { ++TriggerCount; return ETriggerState::Triggered; } } return ETriggerState::Ongoing; } float UGDMInputTriggerPulseWithDelay::CalculateHeldDuration(const UEnhancedPlayerInput* const PlayerInput, const float DeltaTime) const { // We may not have a PlayerInput object during automation tests, so default to 1.0f if we don't have one. // This will mean that TimeDilation has no effect. const float TimeDilation = PlayerInput ? PlayerInput->GetEffectiveTimeDilation() : 1.0f; // Calculates the new held duration, applying time dilation if desired return HeldDuration + (!bAffectedByTimeDilation ? DeltaTime : DeltaTime * TimeDilation); } ``` `SampleProject/Plugins/GameDebugMenu/Source/GameDebugMenu/Private/Input/GDMPadInputWidgetController.cpp`: ```cpp /** * Copyright (c) 2020 akihiko moroi * * This software is released under the MIT License. * (See accompanying file LICENSE.txt or copy at http://opensource.org/licenses/MIT) */ #include "Input/GDMPadInputWidgetController.h" #include #include "Engine/Engine.h" UWorld* UGDMPadInputWidgetController::GetWorld() const { if( !IsValid(OwnerGameDebugMenuWidget) ) { return GWorld; } return OwnerGameDebugMenuWidget->GetWorld(); } ``` `SampleProject/Plugins/GameDebugMenu/Source/GameDebugMenu/Private/Log/GDMOutputDevice.cpp`: ```cpp /** * Copyright (c) 2020 akihiko moroi * * This software is released under the MIT License. * (See accompanying file LICENSE.txt or copy at http://opensource.org/licenses/MIT) */ #include "Log/GDMOutputDevice.h" #include "HAL/CriticalSection.h" #include #include #include "GameDebugMenuSettings.h" FGDMOutputDevice::FGDMOutputDevice() : FOutputDevice() , Logs() { Logs.Reserve(10000); CommandHistory.Reserve(100); GLog->AddOutputDevice(this); } FGDMOutputDevice::~FGDMOutputDevice() { if(GLog != nullptr) { GLog->RemoveOutputDevice(this); } } void FGDMOutputDevice::Serialize(const TCHAR* Data, ELogVerbosity::Type Verbosity, const class FName& Category, const double Time) { this->Serialize(Data, Verbosity, Category); } void FGDMOutputDevice::Serialize(const TCHAR* Data, ELogVerbosity::Type Verbosity, const class FName& Category) { if(Verbosity == ELogVerbosity::SetColor) { // Skip Color Events return; } else { static FName CommandCategory = TEXT("Cmd"); if (Category == CommandCategory) { while(CommandHistory.Num() > GetDefault()->MaxCommandHistoryNum - 1) { CommandHistory.RemoveAt(0); } bool bAdd = true; const FString CheckStr = Data; for (const auto& C : GetDefault()->NoSaveConsoleCommands) { if (CheckStr.Contains(C)) { bAdd = false; break; } } if (bAdd) { CommandHistory.Add(Data); } } /* 時間はUTCで固定し他はエディターの「outputlog」のものと同じものを保持しとく */ static ELogTimes::Type LogTimestampMode = ELogTimes::UTC; // handle multiline strings by breaking them apart by line TArray LineRanges; FString CurrentLogDump = Data; FTextRange::CalculateLineRangesFromString(CurrentLogDump, LineRanges); bool bIsFirstLineInMessage = true; for(const FTextRange& LineRange : LineRanges) { if(!LineRange.IsEmpty()) { FString Line = CurrentLogDump.Mid(LineRange.BeginIndex, LineRange.Len()); Line = Line.ConvertTabsToSpaces(4); // Hard-wrap lines to avoid them being too long static const int32 HardWrapLen = 360; for(int32 CurrentStartIndex = 0; CurrentStartIndex < Line.Len();) { int32 HardWrapLineLen = 0; if(bIsFirstLineInMessage) { FString MessagePrefix = FOutputDeviceHelper::FormatLogLine(Verbosity, Category, nullptr, LogTimestampMode); HardWrapLineLen = FMath::Min(HardWrapLen - MessagePrefix.Len(), Line.Len() - CurrentStartIndex); FString HardWrapLine = Line.Mid(CurrentStartIndex, HardWrapLineLen); Logs.Add(MessagePrefix + HardWrapLine); } else { HardWrapLineLen = FMath::Min(HardWrapLen, Line.Len() - CurrentStartIndex); FString HardWrapLine = Line.Mid(CurrentStartIndex, HardWrapLineLen); Logs.Add(MoveTemp(HardWrapLine)); } bIsFirstLineInMessage = false; CurrentStartIndex += HardWrapLineLen; } } } } } TArray FGDMOutputDevice::GetLogs() const { FScopeLock Lock(&CommandHistoryMutex); return Logs; } TArray FGDMOutputDevice::GetCommandHistory() const { FScopeLock Lock(&CommandHistoryMutex); return CommandHistory; } void FGDMOutputDevice::ClearCommandHistory() { FScopeLock Lock(&CommandHistoryMutex); CommandHistory.Reset(); } ``` `SampleProject/Plugins/GameDebugMenu/Source/GameDebugMenu/Private/Reports/GDMDebugReportRequester.cpp`: ```cpp /** * Copyright (c) 2020 akihiko moroi * * This software is released under the MIT License. * (See accompanying file LICENSE.txt or copy at http://opensource.org/licenses/MIT) */ #include "Reports/GDMDebugReportRequester.h" #include #include #include "Kismet/GameplayStatics.h" const FString AGDMDebugReportRequester::LineBreak = TEXT("\r\n"); AGDMDebugReportRequester::AGDMDebugReportRequester() : Super() , bSendLogs(false) , bSendScreenshotCapture(false) , Subject() , Description() , IssueCategoryIndex(0) , PriorityIndex(0) , AssigneeIndex(0) , ScreenshotImageData() , ScreenshotCapturedDateTime() , TestCount(0) , MaxTestCount(0) , bWasRequestSuccessful(false) { PrimaryActorTick.bCanEverTick = false; PrimaryActorTick.bStartWithTickEnabled = false; } void AGDMDebugReportRequester::StartRequest() { bWasRequestSuccessful = false; } void AGDMDebugReportRequester::SuccessRequest() { bWasRequestSuccessful = true; Destroy(); } void AGDMDebugReportRequester::FailedRequest() { bWasRequestSuccessful = false; Destroy(); } AGameDebugMenuManager* AGDMDebugReportRequester::GetOwnerDebugMenuManager() const { return Cast(GetOwner()); } FString AGDMDebugReportRequester::GetSubject() { return PrefixSubjectString() + Subject + SuffixSubjectString(); } FString AGDMDebugReportRequester::GetDescription() { return PrefixDescriptionString() + Description + SuffixDescriptionString(); } FString AGDMDebugReportRequester::PrefixSubjectString_Implementation() { return FString(); } FString AGDMDebugReportRequester::PrefixDescriptionString_Implementation() { return FString(); } FString AGDMDebugReportRequester::SuffixSubjectString_Implementation() { return FString(); } FString AGDMDebugReportRequester::SuffixDescriptionString_Implementation() { return FString::Printf(TEXT("\n\n====================\n Platform Name : %s\n Build Version : %s\n Build Configuration : %s\n Project Version : %s\n Test Count %d / %d\n") , *UGameplayStatics::GetPlatformName() , *UGameDebugMenuFunctions::GetGDMBuildVersionString() , *UGameDebugMenuFunctions::GetGDMBuildConfigurationString() , *UGameDebugMenuFunctions::GetGDMProjectVersionString() , TestCount, MaxTestCount ); } int32 AGDMDebugReportRequester::GetUTF8StringSize(const FString& Text) { int32 Size = 0; for(const TCHAR Char : Text) { const uint8 Code = static_cast(*TCHAR_TO_UTF8(*FString::Chr(Char))); if((Code >= 0x00) && (Code <= 0x7f)) { Size += 1; } else if((Code >= 0xc2) && (Code <= 0xdf)) { Size += 2; } else if((Code >= 0xe0) && (Code <= 0xef)) { Size += 3; } else if((Code >= 0xf0) && (Code <= 0xf7)) { Size += 4; } else if((Code >= 0xf8) && (Code <= 0xfb)) { Size += 5; } else if((Code >= 0xfc) && (Code <= 0xfd)) { Size += 6; } } return Size; } FString AGDMDebugReportRequester::MakeBoundaryString() { return FString::Printf(TEXT("ReportBoundary_%lld"), FDateTime::Now().ToUnixTimestamp()); } void AGDMDebugReportRequester::AddContentString(const FString& BoundaryKey, const FString& DataName, const FString& ContentType, const FString& SourceData, TArray& OutSendData) { FString WorkStr = LineBreak + TEXT("--") + BoundaryKey + LineBreak; OutSendData.Append((uint8*)TCHAR_TO_UTF8(*WorkStr), GetUTF8StringSize(WorkStr)); WorkStr = TEXT("Content-Disposition: form-data; ") + DataName + LineBreak; OutSendData.Append((uint8*)TCHAR_TO_UTF8(*WorkStr), GetUTF8StringSize(WorkStr)); WorkStr = TEXT("Content-Type: ") + ContentType + LineBreak + LineBreak; OutSendData.Append((uint8*)TCHAR_TO_UTF8(*WorkStr), GetUTF8StringSize(WorkStr)); OutSendData.Append((uint8*)TCHAR_TO_UTF8(*SourceData), GetUTF8StringSize(SourceData)); } void AGDMDebugReportRequester::AddContent(const FString& BoundaryKey, const FString& DataName, const FString& ContentType, const TArray& SourceData, TArray& OutSendData) { FString WorkStr = LineBreak + TEXT("--") + BoundaryKey + LineBreak; OutSendData.Append((uint8*)TCHAR_TO_UTF8(*WorkStr), GetUTF8StringSize(WorkStr)); WorkStr = TEXT("Content-Disposition: form-data; ") + DataName + LineBreak; OutSendData.Append((uint8*)TCHAR_TO_UTF8(*WorkStr), GetUTF8StringSize(WorkStr)); WorkStr = TEXT("Content-Type: ") + ContentType + LineBreak + LineBreak; OutSendData.Append((uint8*)TCHAR_TO_UTF8(*WorkStr), GetUTF8StringSize(WorkStr)); OutSendData.Append(SourceData); } void AGDMDebugReportRequester::AddEndContentString(const FString& BoundaryKey, TArray& OutSendData) { FString WorkStr = LineBreak + TEXT("--") + BoundaryKey + TEXT("--") + LineBreak; OutSendData.Append((uint8*)TCHAR_TO_UTF8(*WorkStr), GetUTF8StringSize(WorkStr)); } ``` `SampleProject/Plugins/GameDebugMenu/Source/GameDebugMenu/Private/Reports/GDMRequesterJira.cpp`: ```cpp /** * Copyright (c) 2020 akihiko moroi * * This software is released under the MIT License. * (See accompanying file LICENSE.txt or copy at http://opensource.org/licenses/MIT) */ #include "Reports/GDMRequesterJira.h" #include #include #include #include #include #include "Dom/JsonObject.h" #include "Serialization/JsonSerializer.h" #include "Serialization/JsonWriter.h" void AGDMRequesterJira::StartRequest() { bWasRequestSuccessful = false; const FGDMJiraSettings& JiraSettings = GetDefault()->JiraSettings; const FString BasicAuthData = TEXT("Basic ") + FBase64::Encode(JiraSettings.UserName + TEXT(":") + JiraSettings.AccessKey); const FString URL = TEXT("https://") + JiraSettings.HostName + TEXT("/rest/api/3/issue"); const TSharedRef Request = FHttpModule::Get().CreateRequest(); Request->OnProcessRequestComplete().BindUObject(this, &AGDMRequesterJira::OnResponseReceived); Request->SetURL(URL); Request->SetVerb(TEXT("POST")); Request->SetHeader(TEXT("Content-Type"), TEXT("application/json")); Request->SetHeader(TEXT("Accept"), TEXT("application/json")); Request->SetHeader(TEXT("Authorization"), BasicAuthData); /*こんな形式のJsonを作ってる { "fields": { "project": { "key": "AAA" }, "priority":{ "name": "BBB" }, "summary": "CCC", "issuetype": { "name": "DDD" }, "description": { "type": "doc", "version": 1, "content": [ { "type": "paragraph", "content": [ { "text": "EEE", "type": "text" } ] } ] }, } } */ TSharedRef JsonRoot = MakeShareable(new FJsonObject()); const TSharedRef JsonFields = MakeShareable(new FJsonObject()); JsonRoot->SetObjectField(TEXT("fields"), JsonFields); { const TSharedRef JsonProject = MakeShareable(new FJsonObject()); JsonFields->SetObjectField(TEXT("project"), JsonProject); { JsonProject->SetStringField(TEXT("key"), JiraSettings.ProjectKeyName); } if(JiraSettings.PriorityNameList.IsValidIndex(PriorityIndex)) { const TSharedRef JsonPriority = MakeShareable(new FJsonObject()); JsonFields->SetObjectField(TEXT("priority"), JsonPriority); { const FString PriorityStr = JiraSettings.PriorityNameList[PriorityIndex].ToString(); JsonPriority->SetStringField(TEXT("name"), PriorityStr); } } const FString AccountId = JiraSettings.GetAssigneeAccountIdByListIndex(AssigneeIndex); if(!AccountId.IsEmpty()) { const TSharedRef JsonAssignee = MakeShareable(new FJsonObject()); JsonFields->SetObjectField(TEXT("assignee"), JsonAssignee); { JsonAssignee->SetStringField(TEXT("accountId"), AccountId); } } JsonFields->SetStringField(TEXT("summary"), GetSubject()); if(JiraSettings.IssueTypeList.IsValidIndex(IssueCategoryIndex)) { const TSharedRef JsonIssueType = MakeShareable(new FJsonObject()); JsonFields->SetObjectField(TEXT("issuetype"), JsonIssueType); { const FString TypeStr = JiraSettings.IssueTypeList[IssueCategoryIndex].ToString(); JsonIssueType->SetStringField(TEXT("name"), TypeStr); } } if( JiraSettings.LabelNameList.IsValidIndex(0)) { TArray< TSharedPtr > JsonLabelArray; for( const auto& Label : JiraSettings.LabelNameList ) { JsonLabelArray.Add(MakeShareable(new FJsonValueString(Label))); } JsonFields->SetArrayField(TEXT("labels"),JsonLabelArray); } const TSharedRef JsonDescription = MakeShareable(new FJsonObject()); JsonFields->SetObjectField(TEXT("description"), JsonDescription); { JsonDescription->SetStringField(TEXT("type"), TEXT("doc")); JsonDescription->SetNumberField(TEXT("version"), 1); const TSharedRef JsonContent = MakeShareable(new FJsonObject()); TArray< TSharedPtr< FJsonValue > > Contents; Contents.Reserve(1); Contents.Emplace(MakeShareable(new FJsonValueObject(JsonContent))); JsonDescription->SetArrayField(TEXT("content"), Contents); { JsonContent->SetStringField(TEXT("type"), TEXT("paragraph")); const TSharedRef JsonContent_2 = MakeShareable(new FJsonObject()); TArray< TSharedPtr< FJsonValue > > Contents_2; Contents_2.Reserve(1); Contents_2.Emplace(MakeShareable(new FJsonValueObject(JsonContent_2))); JsonContent->SetArrayField(TEXT("content"), Contents_2); { JsonContent_2->SetStringField(TEXT("text"), GetDescription()); JsonContent_2->SetStringField(TEXT("type"), TEXT("text")); } } } } FString Content; const auto Writer = TJsonWriterFactory<>::Create(&Content); FJsonSerializer::Serialize(JsonRoot, Writer); Request->SetContentAsString(Content); Request->ProcessRequest(); } void AGDMRequesterJira::RequestUploadContent(const FString& IssueKey) { const FGDMJiraSettings& JiraSettings = GetDefault()->JiraSettings; const FString BasicAuthData = TEXT("Basic ") + FBase64::Encode(JiraSettings.UserName + TEXT(":") + JiraSettings.AccessKey); const FString URL = TEXT("https://") + JiraSettings.HostName + TEXT("/rest/api/3/issue/") + IssueKey + TEXT("/attachments"); const FString BoundaryKey = MakeBoundaryString(); const TSharedRef Request = FHttpModule::Get().CreateRequest(); Request->OnProcessRequestComplete().BindUObject(this, &AGDMRequesterJira::OnResponseReceivedUploaded); Request->SetURL(URL); Request->SetVerb(TEXT("POST")); Request->SetHeader(TEXT("X-Atlassian-Token"), TEXT("no-check")); Request->SetHeader(TEXT("Authorization"), BasicAuthData); Request->SetHeader(TEXT("Content-Type"), FString::Printf(TEXT("multipart/form-data; boundary=%s"), *BoundaryKey)); TArray ContentData; /* 画像データ */ if(bSendScreenshotCapture && (ScreenshotImageData.Num() > 0)) { const FString DataName = FString::Printf(TEXT("name=\"file\"; filename=\"%s.jpg\""), *ScreenshotCapturedDateTime.ToString()); AddContent(BoundaryKey, DataName, TEXT("image/jpeg"), ScreenshotImageData, ContentData); } /* ゲーム内ログ */ if(bSendLogs) { FString GameLog; GetOwnerDebugMenuManager()->GetOutputLogString(GameLog, LineBreak); const FString DataName = FString::Printf(TEXT("name=\"file\"; filename=\"%s.txt\""), *UKismetSystemLibrary::GetGameName()); AddContentString(BoundaryKey, DataName, TEXT("text/plain; charset=UTF-8"), GameLog, ContentData); } AddEndContentString(BoundaryKey, ContentData); Request->SetContent(ContentData); Request->ProcessRequest(); } void AGDMRequesterJira::OnResponseReceived(FHttpRequestPtr Request, FHttpResponsePtr Response, bool bWasSuccessful) { if(bWasSuccessful != false) { const FString ContentAsString = Response->GetContentAsString(); UE_LOG(LogGDM, Log, TEXT("OnResponseReceived: Success %s: ResponseCode %d"), *ContentAsString, Response->GetResponseCode()); const auto JsonReader = TJsonReaderFactory::Create(ContentAsString); TSharedPtr JsonObject = MakeShareable(new FJsonObject()); if(FJsonSerializer::Deserialize(JsonReader, JsonObject)) { const FString IssueKey = JsonObject->GetStringField(TEXT("key")); if( !IssueKey.IsEmpty() && (bSendScreenshotCapture || bSendLogs) ) { RequestUploadContent(IssueKey); } else { SuccessRequest(); } } else { UE_LOG(LogGDM, Warning, TEXT("OnResponseReceived: Deserialize error")); FailedRequest(); } } else { UE_LOG(LogGDM, Error, TEXT("OnResponseReceived: failed to connect")); FailedRequest(); } } void AGDMRequesterJira::OnResponseReceivedUploaded(FHttpRequestPtr Request, FHttpResponsePtr Response, bool bWasSuccessful) { if(bWasSuccessful) { const FString ContentAsString = Response->GetContentAsString(); UE_LOG(LogGDM, Log, TEXT("OnResponseReceivedUploaded: Success %s ResponseCode %d"), *ContentAsString, Response->GetResponseCode()); SuccessRequest(); } else { UE_LOG(LogGDM, Error, TEXT("OnResponseReceivedUploaded: failed to connect")); FailedRequest(); } } ``` `SampleProject/Plugins/GameDebugMenu/Source/GameDebugMenu/Private/Reports/GDMRequesterRedmine.cpp`: ```cpp /** * Copyright (c) 2020 akihiko moroi * * This software is released under the MIT License. * (See accompanying file LICENSE.txt or copy at http://opensource.org/licenses/MIT) */ #include "Reports/GDMRequesterRedmine.h" #include "HttpModule.h" #include "Serialization/JsonWriter.h" #include "Serialization/JsonSerializer.h" #include "GameDebugMenuSettings.h" #include #include "GameDebugMenuManager.h" void AGDMRequesterRedmine::StartRequest() { bWasRequestSuccessful = false; TokenScreenshotCapture.Reset(); TokenLog.Reset(); if(bSendScreenshotCapture && (ScreenshotImageData.Num() > 0)) { RequestUploadScreenshotCapture(); } else if(bSendLogs) { RequestUploadLog(); } else { RequestIssues(); } } void AGDMRequesterRedmine::RequestUploadScreenshotCapture() { FString URL(TEXT("http://")); URL += GetDefault()->RedmineSettings.HostName; URL += TEXT("/redmine/uploads.json?key="); URL += GetDefault()->RedmineSettings.AccessKey; const TSharedRef Request = FHttpModule::Get().CreateRequest(); Request->OnProcessRequestComplete().BindUObject(this, &AGDMRequesterRedmine::OnResponseReceivedUploaded); Request->SetURL(URL); Request->SetVerb(TEXT("POST")); Request->SetHeader(TEXT("Content-Type"), TEXT("application/octet-stream")); Request->SetContent(ScreenshotImageData); Request->ProcessRequest(); } void AGDMRequesterRedmine::RequestUploadLog() { /* ゲーム内ログ */ FString GameLog; GetOwnerDebugMenuManager()->GetOutputLogString(GameLog, LineBreak); FString URL(TEXT("http://")); URL += GetDefault()->RedmineSettings.HostName; URL += TEXT("/redmine/uploads.json?key="); URL += GetDefault()->RedmineSettings.AccessKey; const TSharedRef Request = FHttpModule::Get().CreateRequest(); Request->OnProcessRequestComplete().BindUObject(this, &AGDMRequesterRedmine::OnResponseReceivedLog); Request->SetURL(URL); Request->SetVerb(TEXT("POST")); Request->SetHeader(TEXT("Content-Type"), TEXT("application/octet-stream")); Request->SetContentAsString(GameLog); Request->ProcessRequest(); } void AGDMRequesterRedmine::RequestIssues() { FString URL(TEXT("http://")); URL += GetDefault()->RedmineSettings.HostName; URL += TEXT("/redmine/issues.json?key="); URL += GetDefault()->RedmineSettings.AccessKey; const TSharedRef Request = FHttpModule::Get().CreateRequest(); Request->OnProcessRequestComplete().BindUObject(this, &AGDMRequesterRedmine::OnResponseReceived); Request->SetURL(URL); Request->SetVerb(TEXT("POST")); Request->SetHeader(TEXT("Content-Type"), TEXT("application/json")); const TSharedRef JsonRootObject = MakeShareable(new FJsonObject()); const TSharedRef JsonIssueObject = MakeShareable(new FJsonObject()); JsonRootObject->SetObjectField(TEXT("issue"), JsonIssueObject); JsonIssueObject->SetStringField(TEXT("project_id"), FString::FromInt(GetDefault()->RedmineSettings.ProjectId)); JsonIssueObject->SetStringField(TEXT("tracker_id"), FString::FromInt(IssueCategoryIndex + 1)); JsonIssueObject->SetStringField(TEXT("priority_id"), FString::FromInt(PriorityIndex + 1)); JsonIssueObject->SetStringField(TEXT("subject"), GetSubject()); JsonIssueObject->SetStringField(TEXT("description"), GetDescription()); TArray< TSharedPtr< FJsonValue > > UploadObjects; const TSharedRef JsonUploadObjectCapture = MakeShareable(new FJsonObject()); const TSharedRef JsonUploadObjectLog = MakeShareable(new FJsonObject()); bool bUploads = false; if (!TokenScreenshotCapture.IsEmpty()) { bUploads = true; UploadObjects.Add(MakeShareable(new FJsonValueObject(JsonUploadObjectCapture))); JsonUploadObjectCapture->SetStringField(TEXT("token"), TokenScreenshotCapture); JsonUploadObjectCapture->SetStringField(TEXT("filename"), FString::Printf(TEXT("%s.jpg"), *ScreenshotCapturedDateTime.ToString())); JsonUploadObjectCapture->SetStringField(TEXT("content_type"), TEXT("image/jpg")); } if(!TokenLog.IsEmpty()) { bUploads = true; UploadObjects.Add(MakeShareable(new FJsonValueObject(JsonUploadObjectLog))); JsonUploadObjectLog->SetStringField(TEXT("token"), TokenLog); JsonUploadObjectLog->SetStringField(TEXT("filename"), FString::Printf(TEXT("%s.txt"), *UKismetSystemLibrary::GetGameName())); JsonUploadObjectLog->SetStringField(TEXT("content_type"), TEXT("text/plain")); } if(bUploads) { JsonIssueObject->SetArrayField("uploads", UploadObjects); } FString Content; const auto Writer = TJsonWriterFactory<>::Create(&Content); FJsonSerializer::Serialize(JsonRootObject, Writer); Request->SetContentAsString(Content); Request->ProcessRequest(); } void AGDMRequesterRedmine::OnResponseReceived(FHttpRequestPtr Request, FHttpResponsePtr Response, bool bWasSuccessful) { if (bWasSuccessful != false) { const FString ContentAsString = Response->GetContentAsString(); UE_LOG(LogGDM, Log, TEXT("OnResponseReceived: Success %s: ResponseCode %d"), *ContentAsString, Response->GetResponseCode()); SuccessRequest(); } else { UE_LOG(LogGDM, Error, TEXT("OnResponseReceived: failed to connect")); FailedRequest(); } } void AGDMRequesterRedmine::OnResponseReceivedUploaded(FHttpRequestPtr Request, FHttpResponsePtr Response, bool bWasSuccessful) { if (bWasSuccessful) { const FString ContentAsString = Response->GetContentAsString(); UE_LOG(LogGDM, Log, TEXT("OnResponseReceivedUploaded: Success %s: ResponseCode %d"), *ContentAsString, Response->GetResponseCode()); const auto JsonReader = TJsonReaderFactory::Create(ContentAsString); TSharedPtr JsonObject = MakeShareable(new FJsonObject()); if (FJsonSerializer::Deserialize(JsonReader, JsonObject)) { const auto UploadObj = JsonObject->GetObjectField(TEXT("upload")); TokenScreenshotCapture = UploadObj->GetStringField(TEXT("token")); if(bSendLogs) { RequestUploadLog(); } else { RequestIssues(); } } else { UE_LOG(LogGDM, Warning, TEXT("OnResponseReceivedUploaded: Deserialize error")); FailedRequest(); } } else { UE_LOG(LogGDM, Error, TEXT("OnResponseReceivedUploaded: failed to connect")); FailedRequest(); } } void AGDMRequesterRedmine::OnResponseReceivedLog(FHttpRequestPtr Request, FHttpResponsePtr Response, bool bWasSuccessful) { if(bWasSuccessful) { const FString ContentAsString = Response->GetContentAsString(); UE_LOG(LogGDM, Log, TEXT("OnResponseReceivedLog: Success %s: ResponseCode %d"), *ContentAsString, Response->GetResponseCode()); const auto JsonReader = TJsonReaderFactory::Create(ContentAsString); TSharedPtr JsonObject = MakeShareable(new FJsonObject()); if(FJsonSerializer::Deserialize(JsonReader, JsonObject)) { const auto UploadObj = JsonObject->GetObjectField(TEXT("upload")); TokenLog = UploadObj->GetStringField(TEXT("token")); RequestIssues(); } else { UE_LOG(LogGDM, Warning, TEXT("OnResponseReceivedLog: Deserialize error")); FailedRequest(); } } else { UE_LOG(LogGDM, Error, TEXT("OnResponseReceivedLog: failed to connect")); FailedRequest(); } } ``` `SampleProject/Plugins/GameDebugMenu/Source/GameDebugMenu/Private/Reports/GDMRequesterTrello.cpp`: ```cpp /** * Copyright (c) 2020 akihiko moroi * * This software is released under the MIT License. * (See accompanying file LICENSE.txt or copy at http://opensource.org/licenses/MIT) */ #include "Reports/GDMRequesterTrello.h" #include "HttpModule.h" #include "Serialization/JsonSerializer.h" #include "GameDebugMenuSettings.h" #include #include "GameDebugMenuManager.h" void AGDMRequesterTrello::StartRequest() { bWasRequestSuccessful = false; const FGDMTrelloSettings& TrelloSettings = GetDefault()->TrelloSettings; AttachmentCardListID.Reset(); FString URL(TEXT("https://api.trello.com/1/cards?")); URL += TEXT("?pos=top"); URL += TEXT("&idList="); URL += TrelloSettings.CardListIDs[IssueCategoryIndex]; URL += TEXT("&key="); URL += TrelloSettings.AccessKey; URL += TEXT("&token="); URL += TrelloSettings.AccessToken; const TSharedRef Request = FHttpModule::Get().CreateRequest(); Request->OnProcessRequestComplete().BindUObject(this, &AGDMRequesterTrello::OnResponseReceived); Request->SetURL(URL); Request->SetVerb(TEXT("POST")); const FString BoundaryKey = MakeBoundaryString(); Request->SetHeader(TEXT("Content-Type"), FString::Printf(TEXT("multipart/form-data; boundary=%s"), *BoundaryKey)); TArray ContentData; AddContentString(BoundaryKey, TEXT("name=\"name\";"), TEXT("text/plain; charset=UTF-8"), GetSubject(), ContentData); AddContentString(BoundaryKey, TEXT("name=\"desc\";"), TEXT("text/plain; charset=UTF-8"), GetDescription(), ContentData); AddEndContentString(BoundaryKey, ContentData); Request->SetContent(ContentData); Request->ProcessRequest(); } void AGDMRequesterTrello::RequestUploadScreenshotCapture() { const FGDMTrelloSettings& TrelloSettings = GetDefault()->TrelloSettings; FString URL(TEXT("https://api.trello.com/1/cards/")); URL += AttachmentCardListID; URL += TEXT("/attachments?"); URL += TEXT("&key="); URL += TrelloSettings.AccessKey; URL += TEXT("&token="); URL += TrelloSettings.AccessToken; const TSharedRef Request = FHttpModule::Get().CreateRequest(); Request->OnProcessRequestComplete().BindUObject(this, &AGDMRequesterTrello::OnResponseReceivedUploaded); Request->SetURL(URL); Request->SetVerb(TEXT("POST")); const FString BoundaryKey = MakeBoundaryString(); Request->SetHeader(TEXT("Content-Type"), FString::Printf(TEXT("multipart/form-data; boundary=%s"), *BoundaryKey)); TArray ContentData; /* 画像データ */ const FString DataName = FString::Printf(TEXT("name=\"file\"; filename=\"%s.jpg\""),*ScreenshotCapturedDateTime.ToString()); AddContent(BoundaryKey,DataName, TEXT("image/jpeg"), ScreenshotImageData, ContentData); AddEndContentString(BoundaryKey, ContentData); Request->SetContent(ContentData); Request->ProcessRequest(); } void AGDMRequesterTrello::RequestUploadLog() { const FGDMTrelloSettings& TrelloSettings = GetDefault()->TrelloSettings; FString URL(TEXT("https://api.trello.com/1/cards/")); URL += AttachmentCardListID; URL += TEXT("/attachments?"); URL += TEXT("&key="); URL += TrelloSettings.AccessKey; URL += TEXT("&token="); URL += TrelloSettings.AccessToken; const TSharedRef Request = FHttpModule::Get().CreateRequest(); Request->OnProcessRequestComplete().BindUObject(this, &AGDMRequesterTrello::OnResponseReceivedLog); Request->SetURL(URL); Request->SetVerb(TEXT("POST")); const FString BoundaryKey = MakeBoundaryString(); Request->SetHeader(TEXT("Content-Type"), FString::Printf(TEXT("multipart/form-data; boundary=%s"), *BoundaryKey)); TArray ContentData; /* ゲーム内ログ */ FString GameLog; GetOwnerDebugMenuManager()->GetOutputLogString(GameLog, LineBreak); const FString DataName = FString::Printf(TEXT("name=\"file\"; filename=\"%s.txt\""), *UKismetSystemLibrary::GetGameName()); AddContentString(BoundaryKey, DataName, TEXT("text/plain; charset=UTF-8"), GameLog, ContentData); AddEndContentString(BoundaryKey, ContentData); Request->SetContent(ContentData); Request->ProcessRequest(); } void AGDMRequesterTrello::OnResponseReceived(FHttpRequestPtr Request, FHttpResponsePtr Response, bool bWasSuccessful) { if (bWasSuccessful) { const FString ContentAsString = Response->GetContentAsString(); UE_LOG(LogGDM, Log, TEXT("OnResponseReceived Success [%s] ResponseCode[%d]"), *ContentAsString, Response->GetResponseCode()); const auto JsonReader = TJsonReaderFactory::Create(ContentAsString); TSharedPtr JsonObject = MakeShareable(new FJsonObject()); if (FJsonSerializer::Deserialize(JsonReader, JsonObject)) { AttachmentCardListID = JsonObject->GetStringField(TEXT("id")); if(!AttachmentCardListID.IsEmpty()) { if( bSendScreenshotCapture && (ScreenshotImageData.Num() > 0) ) { /* 画像データを送信 */ RequestUploadScreenshotCapture(); } else if(bSendLogs) { /* ログを送信 */ RequestUploadLog(); } else { SuccessRequest(); } } else { SuccessRequest(); } } else { UE_LOG(LogGDM, Warning, TEXT("OnResponseReceived: Deserialize error")); FailedRequest(); } } else { UE_LOG(LogGDM, Error, TEXT("OnResponseReceived: failed to connect")); FailedRequest(); } } void AGDMRequesterTrello::OnResponseReceivedUploaded(FHttpRequestPtr Request, FHttpResponsePtr Response, bool bWasSuccessful) { if (bWasSuccessful) { const FString ContentAsString = Response->GetContentAsString(); UE_LOG(LogGDM, Log, TEXT("OnResponseReceivedUploaded: Success %s: ResponseCode %d"), *ContentAsString, Response->GetResponseCode()); const auto JsonReader = TJsonReaderFactory::Create(ContentAsString); TSharedPtr JsonObject = MakeShareable(new FJsonObject()); if(FJsonSerializer::Deserialize(JsonReader, JsonObject)) { if(bSendLogs) { /* ログデータを送信 */ RequestUploadLog(); } else { SuccessRequest(); } } else { UE_LOG(LogGDM, Warning, TEXT("OnResponseReceivedUploaded: Deserialize error")); FailedRequest(); } } else { UE_LOG(LogGDM, Error, TEXT("OnResponseReceivedUploaded: failed to connect")); FailedRequest(); } } void AGDMRequesterTrello::OnResponseReceivedLog(FHttpRequestPtr Request, FHttpResponsePtr Response, bool bWasSuccessful) { if(bWasSuccessful) { const FString ContentAsString = Response->GetContentAsString(); UE_LOG(LogGDM, Log, TEXT("OnResponseReceivedLog: Success %s: ResponseCode %d"), *ContentAsString, Response->GetResponseCode()); SuccessRequest(); } else { UE_LOG(LogGDM, Error, TEXT("OnResponseReceivedLog: failed to connect")); FailedRequest(); } } ``` `SampleProject/Plugins/GameDebugMenu/Source/GameDebugMenu/Private/Widgets/GDMButton.cpp`: ```cpp /** * Copyright (c) 2020 akihiko moroi * * This software is released under the MIT License. * (See accompanying file LICENSE.txt or copy at http://opensource.org/licenses/MIT) */ #include "Widgets/GDMButton.h" #include "Widgets/Input/SButton.h" #include #include #include "Components/ButtonSlot.h" #define LOCTEXT_NAMESPACE "GameDebugMenu" UGDMButton::UGDMButton(const FObjectInitializer& ObjectInitializer) : Super(ObjectInitializer) { InitIsFocusable(false); /* デフォのDownAndUpだと別UserWidget上でボタンをクリックしたときに1度無視され2回クリックしないと応答しないことがあるため変更 */ SetClickMethod(EButtonClickMethod::Type::MouseDown); } #if WITH_EDITOR const FText UGDMButton::GetPaletteCategory() { return LOCTEXT("GDM", "GDM"); } #endif TSharedRef UGDMButton::RebuildWidget() { MyButton = SNew(SButton) .OnClicked(BIND_UOBJECT_DELEGATE(FOnClicked, GDMSlateHandleClicked)) .OnPressed(BIND_UOBJECT_DELEGATE(FSimpleDelegate, GDMSlateHandlePressed)) .OnReleased(BIND_UOBJECT_DELEGATE(FSimpleDelegate, GDMSlateHandleReleased)) .OnHovered_UObject(this, &ThisClass::GDMSlateHandleHovered) .OnUnhovered_UObject(this, &ThisClass::GDMSlateHandleUnhovered) .ButtonStyle(&GetStyle()) .ClickMethod(GetClickMethod()) .TouchMethod(GetTouchMethod()) .IsFocusable(GetIsFocusable()) ; if(GetChildrenCount() > 0) { Cast(GetContentSlot())->BuildSlot(MyButton.ToSharedRef()); } return MyButton.ToSharedRef(); } FReply UGDMButton::GDMSlateHandleClicked() { if(const AGameDebugMenuManager* DebugMenuManager = UGameDebugMenuFunctions::GetGameDebugMenuManager(this)) { if(DebugMenuManager->IsInputIgnored()) { return FReply::Unhandled(); } } return SlateHandleClicked(); } void UGDMButton::GDMSlateHandlePressed() { if(const AGameDebugMenuManager* DebugMenuManager = UGameDebugMenuFunctions::GetGameDebugMenuManager(this)) { if(DebugMenuManager->IsInputIgnored()) { return; } } SlateHandlePressed(); } void UGDMButton::GDMSlateHandleReleased() { if(const AGameDebugMenuManager* DebugMenuManager = UGameDebugMenuFunctions::GetGameDebugMenuManager(this)) { if(DebugMenuManager->IsInputIgnored()) { return; } } SlateHandleReleased(); } void UGDMButton::GDMSlateHandleHovered() { if(const AGameDebugMenuManager* DebugMenuManager = UGameDebugMenuFunctions::GetGameDebugMenuManager(this)) { if(DebugMenuManager->IsInputIgnored()) { return; } } SlateHandleHovered(); } void UGDMButton::GDMSlateHandleUnhovered() { if(const AGameDebugMenuManager* DebugMenuManager = UGameDebugMenuFunctions::GetGameDebugMenuManager(this)) { if(DebugMenuManager->IsInputIgnored()) { return; } } SlateHandleUnhovered(); } #undef LOCTEXT_NAMESPACE ``` `SampleProject/Plugins/GameDebugMenu/Source/GameDebugMenu/Private/Widgets/GDMComboBoxString.cpp`: ```cpp /** * Copyright (c) 2020 akihiko moroi * * This software is released under the MIT License. * (See accompanying file LICENSE.txt or copy at http://opensource.org/licenses/MIT) */ #include "Widgets/GDMComboBoxString.h" #include #define LOCTEXT_NAMESPACE "GDMComboBoxString" UGDMComboBoxString::UGDMComboBoxString(const FObjectInitializer& ObjectInitializer) : Super(ObjectInitializer) { InitIsFocusable(false); if(!IsRunningDedicatedServer()) { InitFont(FSlateFontInfo(GetDefault()->GetDebugMenuFont(), 14, FName("Bold"))); } } #if WITH_EDITOR const FText UGDMComboBoxString::GetPaletteCategory() { return LOCTEXT("GDM", "GDM"); } #endif #undef LOCTEXT_NAMESPACE ``` `SampleProject/Plugins/GameDebugMenu/Source/GameDebugMenu/Private/Widgets/GDMDebugReportWidget.cpp`: ```cpp /** * Copyright (c) 2020 akihiko moroi * * This software is released under the MIT License. * (See accompanying file LICENSE.txt or copy at http://opensource.org/licenses/MIT) */ #include "Widgets/GDMDebugReportWidget.h" #include "GameDebugMenuSettings.h" #include "Reports/GDMRequesterRedmine.h" #include "Reports/GDMRequesterTrello.h" #include "GameDebugMenuManager.h" #include "GameDebugMenuFunctions.h" #include "ImageUtils.h" #include "Engine/Texture2D.h" UGDMDebugReportWidget::UGDMDebugReportWidget(const FObjectInitializer& ObjectInitializer) : Super(ObjectInitializer) , OnReceivedReportDispatcher() , ScreenshotTexture(nullptr) , ScreenshotImageData() , TestCount(0) , MaxTestCount(0) , ScreenshotCapturedDateTime() , CurrentDebugReportRequester(nullptr) { } void UGDMDebugReportWidget::SendDebugReport(const FString& Subject, const FString& Description, int32 IssueCategoryIndex, int32 PriorityIndex, int32 AssigneeIndex,bool bSendLogs, bool bSendScreenshotCapture) { AGameDebugMenuManager* DebugMenuManager = UGameDebugMenuFunctions::GetGameDebugMenuManager(this); check(DebugMenuManager != nullptr); const TSubclassOf* RequesterClass = GetDefault()->GetDebugReportRequesterClass(); if( RequesterClass == nullptr ) { OnReceivedReportDispatcher.Broadcast(false); return; } CurrentDebugReportRequester = GetWorld()->SpawnActor(*RequesterClass); CurrentDebugReportRequester->SetOwner(DebugMenuManager); if (CurrentDebugReportRequester.IsValid()) { CurrentDebugReportRequester->bSendLogs = bSendLogs; CurrentDebugReportRequester->bSendScreenshotCapture = bSendScreenshotCapture; CurrentDebugReportRequester->Subject = Subject; CurrentDebugReportRequester->Description = Description; CurrentDebugReportRequester->IssueCategoryIndex = IssueCategoryIndex; CurrentDebugReportRequester->PriorityIndex = PriorityIndex; CurrentDebugReportRequester->AssigneeIndex = AssigneeIndex; CurrentDebugReportRequester->ScreenshotImageData = ScreenshotImageData; CurrentDebugReportRequester->ScreenshotCapturedDateTime = ScreenshotCapturedDateTime; CurrentDebugReportRequester->TestCount = TestCount; CurrentDebugReportRequester->MaxTestCount = MaxTestCount; CurrentDebugReportRequester->OnDestroyed.AddDynamic(this, &UGDMDebugReportWidget::OnRequesterDestroyed); CurrentDebugReportRequester->StartRequest(); } } TArray UGDMDebugReportWidget::GetIssueCategoryNameList() { return GetDefault()->GetIssueCategoryNameList(); } TArray UGDMDebugReportWidget::GetPriorityNameList() { return GetDefault()->GetPriorityNameList(); } TArray UGDMDebugReportWidget::GetAssigneeNameList() { return GetDefault()->GetAssigneeNameList(); } int32 UGDMDebugReportWidget::GetDefaultIssueCategoryIndex() { return GetDefault()->GetDefaultIssueCategoryIndex(); } int32 UGDMDebugReportWidget::GetDefaultPriorityIndex() { return GetDefault()->GetDefaultPriorityIndex(); } void UGDMDebugReportWidget::OnScreenshotCaptured(int32 Width, int32 Height, const TArray& Bitmap) { /* Debugレポートようにキャッシュ */ ScreenshotImageData.Reset(); const TArray BitmapCopy(Bitmap); FImageUtils::ThumbnailCompressImageArray(Width, Height, BitmapCopy, ScreenshotImageData); UTexture2D* NewTexture = FImageUtils::ImportBufferAsTexture2D(ScreenshotImageData); NewTexture->SRGB = true; // /* テクスチャを生成 */ // UTexture2D* NewTexture = UTexture2D::CreateTransient(Width, Height, EPixelFormat::PF_B8G8R8A8); // NewTexture->SRGB = true; // // /* テクスチャをコピーする */ // void* TextureData = NewTexture->GetPlatformData()->Mips[0].BulkData.Lock(LOCK_READ_WRITE); // const int32 TextureDataSize = Bitmap.Num() * 4; // FMemory::Memcpy(TextureData, Bitmap.GetData(), TextureDataSize); // NewTexture->GetPlatformData()->Mips[0].BulkData.Unlock(); // // /* 更新 */ // NewTexture->UpdateResource(); ScreenshotTexture = NewTexture; ScreenshotCapturedDateTime = FDateTime::Now(); OnCreatedScreenshotTexture(ScreenshotTexture); } void UGDMDebugReportWidget::OnRequesterDestroyed(AActor* DestroyedActor) { AGDMDebugReportRequester* Requester = Cast(DestroyedActor); if (IsValid(Requester)) { OnReceivedReportDispatcher.Broadcast(Requester->bWasRequestSuccessful); } else { OnReceivedReportDispatcher.Broadcast(false); } CurrentDebugReportRequester = nullptr; } bool UGDMDebugReportWidget::IsRequesting() { return CurrentDebugReportRequester.IsValid(); } ``` `SampleProject/Plugins/GameDebugMenu/Source/GameDebugMenu/Private/Widgets/GDMFunctionWidget.cpp`: ```cpp /** * Copyright (c) 2020 akihiko moroi * * This software is released under the MIT License. * (See accompanying file LICENSE.txt or copy at http://opensource.org/licenses/MIT) */ #include "Widgets/GDMFunctionWidget.h" #include "GameDebugMenuFunctions.h" bool UGDMFunctionWidget::TryCallObjectFunction(FName EventName) { return GDMProcessEvent(EventName, nullptr); } bool UGDMFunctionWidget::GDMProcessEvent(FName EventName, void* Parms) { if(!IsValid(TargetObject)) { UE_LOG(LogGDM, Warning, TEXT("GDMFunctionWidget ProcessEvent: Not found Object")); return false; } if(EventName == NAME_None) { UE_LOG(LogGDM, Warning, TEXT("GDMFunctionWidget ProcessEvent: Not found EventName")); return false; } UFunction* TargetFunction = TargetObject->FindFunction(EventName); if(!IsValid(TargetFunction)) { UE_LOG(LogGDM, Warning, TEXT("GDMFunctionWidget ProcessEvent: Not found Function")); return false; } if(TargetFunction->NumParms != 0) { UE_LOG(LogGDM, Warning, TEXT("GDMFunctionWidget ProcessEvent: Arguments are not supported: %d"), TargetFunction->NumParms); return false; } TargetObject->ProcessEvent(TargetFunction, nullptr); UGameDebugMenuFunctions::GetGameDebugMenuManager(this)->CallExecuteProcessEventDispatcher(EventName, TargetObject); return true; } ``` `SampleProject/Plugins/GameDebugMenu/Source/GameDebugMenu/Private/Widgets/GDMIntSpinBox.cpp`: ```cpp /** * Copyright (c) 2020 akihiko moroi * * This software is released under the MIT License. * (See accompanying file LICENSE.txt or copy at http://opensource.org/licenses/MIT) */ #include "Widgets/GDMIntSpinBox.h" #include "UObject/ConstructorHelpers.h" #include "Engine/Font.h" #include #define LOCTEXT_NAMESPACE "UMG" UGDMIntSpinBox::UGDMIntSpinBox(const FObjectInitializer& ObjectInitializer) : Super(ObjectInitializer) { if (!IsRunningDedicatedServer()) { Font = FSlateFontInfo(GetDefault()->GetDebugMenuFont(), 12, FName("Bold")); } // Grab other defaults from slate arguments. SSpinBox::FArguments Defaults; Value = Defaults._Value.Get(); MinValue = Defaults._MinValue.Get().Get(0); MaxValue = Defaults._MaxValue.Get().Get(0); MinSliderValue = Defaults._MinSliderValue.Get().Get(0); MaxSliderValue = Defaults._MaxSliderValue.Get().Get(0); Delta = Defaults._Delta.Get(); SliderExponent = Defaults._SliderExponent.Get(); MinDesiredWidth = Defaults._MinDesiredWidth.Get(); ClearKeyboardFocusOnCommit = Defaults._ClearKeyboardFocusOnCommit.Get(); SelectAllTextOnCommit = Defaults._SelectAllTextOnCommit.Get(); WidgetStyle = *Defaults._Style; ForegroundColor = FSlateColor(FLinearColor::Black); } void UGDMIntSpinBox::ReleaseSlateResources(bool bReleaseChildren) { Super::ReleaseSlateResources(bReleaseChildren); MySpinBox.Reset(); } TSharedRef UGDMIntSpinBox::RebuildWidget() { MySpinBox = SNew(SSpinBox) .Style(&WidgetStyle) .Font(Font) .ClearKeyboardFocusOnCommit(ClearKeyboardFocusOnCommit) .SelectAllTextOnCommit(SelectAllTextOnCommit) .Justification(Justification) .OnValueChanged(BIND_UOBJECT_DELEGATE(FOnInt32ValueChanged, HandleOnValueChanged)) .OnValueCommitted(BIND_UOBJECT_DELEGATE(FOnInt32ValueCommitted, HandleOnValueCommitted)) .OnBeginSliderMovement(BIND_UOBJECT_DELEGATE(FSimpleDelegate, HandleOnBeginSliderMovement)) .OnEndSliderMovement(BIND_UOBJECT_DELEGATE(FOnInt32ValueChanged, HandleOnEndSliderMovement)) ; return MySpinBox.ToSharedRef(); } void UGDMIntSpinBox::SynchronizeProperties() { Super::SynchronizeProperties(); MySpinBox->SetDelta(Delta); MySpinBox->SetSliderExponent(SliderExponent); MySpinBox->SetMinDesiredWidth(MinDesiredWidth); MySpinBox->SetForegroundColor(ForegroundColor); // Set optional values bOverride_MinValue ? SetMinValue(MinValue) : ClearMinValue(); bOverride_MaxValue ? SetMaxValue(MaxValue) : ClearMaxValue(); bOverride_MinSliderValue ? SetMinSliderValue(MinSliderValue) : ClearMinSliderValue(); bOverride_MaxSliderValue ? SetMaxSliderValue(MaxSliderValue) : ClearMaxSliderValue(); // Always set the value last so that the max/min values are taken into account. TAttribute ValueBinding = PROPERTY_BINDING(int32, Value); MySpinBox->SetValue(ValueBinding); } int32 UGDMIntSpinBox::GetValue() const { if (MySpinBox.IsValid()) { return MySpinBox->GetValue(); } return Value; } void UGDMIntSpinBox::SetValue(int32 InValue) { Value = InValue; if (MySpinBox.IsValid()) { MySpinBox->SetValue(InValue); } } // MIN VALUE int32 UGDMIntSpinBox::GetMinValue() const { int32 ReturnVal = TNumericLimits::Lowest(); if (MySpinBox.IsValid()) { ReturnVal = MySpinBox->GetMinValue(); } else if (bOverride_MinValue) { ReturnVal = MinValue; } return ReturnVal; } void UGDMIntSpinBox::SetMinValue(int32 InMinValue) { bOverride_MinValue = true; MinValue = InMinValue; if (MySpinBox.IsValid()) { MySpinBox->SetMinValue(InMinValue); } } void UGDMIntSpinBox::ClearMinValue() { bOverride_MinValue = false; if (MySpinBox.IsValid()) { MySpinBox->SetMinValue(TOptional()); } } // MAX VALUE int32 UGDMIntSpinBox::GetMaxValue() const { int32 ReturnVal = TNumericLimits::Max(); if (MySpinBox.IsValid()) { ReturnVal = MySpinBox->GetMaxValue(); } else if (bOverride_MaxValue) { ReturnVal = MaxValue; } return ReturnVal; } void UGDMIntSpinBox::SetMaxValue(int32 InMaxValue) { bOverride_MaxValue = true; MaxValue = InMaxValue; if (MySpinBox.IsValid()) { MySpinBox->SetMaxValue(InMaxValue); } } void UGDMIntSpinBox::ClearMaxValue() { bOverride_MaxValue = false; if (MySpinBox.IsValid()) { MySpinBox->SetMaxValue(TOptional()); } } // MIN SLIDER VALUE int32 UGDMIntSpinBox::GetMinSliderValue() const { int32 ReturnVal = TNumericLimits::Min(); if (MySpinBox.IsValid()) { ReturnVal = MySpinBox->GetMinSliderValue(); } else if (bOverride_MinSliderValue) { ReturnVal = MinSliderValue; } return ReturnVal; } void UGDMIntSpinBox::SetMinSliderValue(int32 InMinSliderValue) { bOverride_MinSliderValue = true; MinSliderValue = InMinSliderValue; if (MySpinBox.IsValid()) { MySpinBox->SetMinSliderValue(InMinSliderValue); } } void UGDMIntSpinBox::ClearMinSliderValue() { bOverride_MinSliderValue = false; if (MySpinBox.IsValid()) { MySpinBox->SetMinSliderValue(TOptional()); } } // MAX SLIDER VALUE int32 UGDMIntSpinBox::GetMaxSliderValue() const { int32 ReturnVal = TNumericLimits::Max(); if (MySpinBox.IsValid()) { ReturnVal = MySpinBox->GetMaxSliderValue(); } else if (bOverride_MaxSliderValue) { ReturnVal = MaxSliderValue; } return ReturnVal; } void UGDMIntSpinBox::SetMaxSliderValue(int32 InMaxSliderValue) { bOverride_MaxSliderValue = true; MaxSliderValue = InMaxSliderValue; if (MySpinBox.IsValid()) { MySpinBox->SetMaxSliderValue(InMaxSliderValue); } } void UGDMIntSpinBox::ClearMaxSliderValue() { bOverride_MaxSliderValue = false; if (MySpinBox.IsValid()) { MySpinBox->SetMaxSliderValue(TOptional()); } } void UGDMIntSpinBox::SetForegroundColor(FSlateColor InForegroundColor) { ForegroundColor = InForegroundColor; if ( MySpinBox.IsValid() ) { MySpinBox->SetForegroundColor(ForegroundColor); } } // Event handlers void UGDMIntSpinBox::HandleOnValueChanged(int32 InValue) { if ( !IsDesignTime() ) { OnValueChanged.Broadcast(InValue); } } void UGDMIntSpinBox::HandleOnValueCommitted(int32 InValue, ETextCommit::Type CommitMethod) { if ( !IsDesignTime() ) { OnValueCommitted.Broadcast(InValue, CommitMethod); } } void UGDMIntSpinBox::HandleOnBeginSliderMovement() { if ( !IsDesignTime() ) { OnBeginSliderMovement.Broadcast(); } } void UGDMIntSpinBox::HandleOnEndSliderMovement(int32 InValue) { if ( !IsDesignTime() ) { OnEndSliderMovement.Broadcast(InValue); } } void UGDMIntSpinBox::PostLoad() { Super::PostLoad(); if ( GetLinkerUEVersion() < VER_UE4_DEPRECATE_UMG_STYLE_ASSETS ) { if ( Style_DEPRECATED != nullptr ) { const FSpinBoxStyle* StylePtr = Style_DEPRECATED->GetStyle(); if ( StylePtr != nullptr ) { WidgetStyle = *StylePtr; } Style_DEPRECATED = nullptr; } } } #if WITH_EDITOR const FText UGDMIntSpinBox::GetPaletteCategory() { return LOCTEXT("GDM", "GDM"); } #endif ///////////////////////////////////////////////////// #undef LOCTEXT_NAMESPACE ``` `SampleProject/Plugins/GameDebugMenu/Source/GameDebugMenu/Private/Widgets/GDMPropertyWidget.cpp`: ```cpp /** * Copyright (c) 2020 akihiko moroi * * This software is released under the MIT License. * (See accompanying file LICENSE.txt or copy at http://opensource.org/licenses/MIT) */ #include "Widgets/GDMPropertyWidget.h" #include "GameDebugMenuFunctions.h" #include "GameDebugMenuTypes.h" void UGDMPropertyWidget::NativeConstruct() { Super::NativeConstruct(); } void UGDMPropertyWidget::NativeDestruct() { Super::NativeDestruct(); } void UGDMPropertyWidget::NativeTick(const FGeometry& MyGeometry, float InDeltaTime) { Super::NativeTick(MyGeometry, InDeltaTime); /* 無操作状態からリセットまでの猶予(秒) */ constexpr float InactiveResetThreshold = 0.18f; if (bStartChangeAmount) { /* 変化量の時間計測 */ if (!bChangedMaxChangeAmount) { ElapsedTime -= InDeltaTime; if (ElapsedTime <= 0.0f) { ElapsedTime = 0.0f; ChangeAmount = PropertyConfigInfo.MaxChangeAmount; bChangedMaxChangeAmount = true; } } InactiveElapsedTime += InDeltaTime; if (InactiveElapsedTime >= InactiveResetThreshold) { /* 一定時間StartChangeAmountTimeが呼ばれなくなったのでもう数値が変動してないと判断し戻す */ ResetChangeAmountTime(); } } } void UGDMPropertyWidget::StartChangeAmountTime() { if (!bStartChangeAmount) { bStartChangeAmount = true; bChangedMaxChangeAmount = false; ChangeAmount = PropertyConfigInfo.DefaultChangeAmount; ElapsedTime = PropertyConfigInfo.MaxChangeAmountTime; } /* 呼ばれるたびに無操作時間をリセット */ InactiveElapsedTime = 0.0f; } void UGDMPropertyWidget::ResetChangeAmountTime() { if (!bStartChangeAmount) { return; } bStartChangeAmount = false; bChangedMaxChangeAmount = false; ChangeAmount = PropertyConfigInfo.DefaultChangeAmount; InactiveElapsedTime = 0.0f; } bool UGDMPropertyWidget::GetPropertyValue_Bool(bool& bHasProperty) { bHasProperty = false; if(!IsValid(TargetObject)) { return false; } const FBoolProperty* BoolProp = CastField(TargetObject->GetClass()->FindPropertyByName(PropertyName)); if(BoolProp == nullptr) { return false; } bHasProperty = true; return BoolProp->GetPropertyValue(BoolProp->ContainerPtrToValuePtr(TargetObject)); } void UGDMPropertyWidget::SetPropertyValue_Bool(bool bNewValue, bool& bHasProperty) { bHasProperty = false; if(!IsValid(TargetObject)) { return; } const FBoolProperty* BoolProp = CastField(TargetObject->GetClass()->FindPropertyByName(PropertyName)); if(BoolProp == nullptr) { return; } bHasProperty = true; const bool bOldValue = BoolProp->GetPropertyValue(BoolProp->ContainerPtrToValuePtr(TargetObject)); if (bNewValue != bOldValue) { BoolProp->SetPropertyValue(BoolProp->ContainerPtrToValuePtr(TargetObject), bNewValue); UGameDebugMenuFunctions::GetGameDebugMenuManager(this)->CallChangePropertyBoolDispatcher(PropertyName, TargetObject, bNewValue, bOldValue, PropertySaveKey); } } float UGDMPropertyWidget::GetPropertyValue_Float(bool& bHasProperty) { bHasProperty = false; if(!IsValid(TargetObject)) { return 0.0f; } const FFloatProperty* FloatProp = CastField(TargetObject->GetClass()->FindPropertyByName(PropertyName)); if(FloatProp == nullptr) { if(const FDoubleProperty* DoubleProp = CastField(TargetObject->GetClass()->FindPropertyByName(PropertyName))) { bHasProperty = true; return DoubleProp->GetPropertyValue(DoubleProp->ContainerPtrToValuePtr(TargetObject)); } return 0.0f; } bHasProperty = true; return FloatProp->GetPropertyValue(FloatProp->ContainerPtrToValuePtr(TargetObject)); } void UGDMPropertyWidget::SetPropertyValue_Float(float NewValue, bool& bHasProperty) { bHasProperty = false; if(!IsValid(TargetObject)) { return; } const FFloatProperty* FloatProp = CastField(TargetObject->GetClass()->FindPropertyByName(PropertyName)); if(FloatProp == nullptr) { if(const FDoubleProperty* DoubleProp = CastField(TargetObject->GetClass()->FindPropertyByName(PropertyName))) { bHasProperty = true; const float OldValue = DoubleProp->GetPropertyValue(DoubleProp->ContainerPtrToValuePtr(TargetObject)); if (FMath::IsNearlyEqual(NewValue, OldValue) == false) { DoubleProp->SetPropertyValue(DoubleProp->ContainerPtrToValuePtr(TargetObject), NewValue); UGameDebugMenuFunctions::GetGameDebugMenuManager(this)->CallChangePropertyFloatDispatcher(PropertyName, TargetObject, NewValue, OldValue, PropertySaveKey); } } return; } bHasProperty = true; const float OldValue = FloatProp->GetPropertyValue(FloatProp->ContainerPtrToValuePtr(TargetObject)); if (FMath::IsNearlyEqual(NewValue, OldValue) == false) { FloatProp->SetPropertyValue(FloatProp->ContainerPtrToValuePtr(TargetObject), NewValue); UGameDebugMenuFunctions::GetGameDebugMenuManager(this)->CallChangePropertyFloatDispatcher(PropertyName, TargetObject, NewValue, OldValue, PropertySaveKey); } } int32 UGDMPropertyWidget::GetPropertyValue_Int(bool& bHasProperty) { bHasProperty = false; if(!IsValid(TargetObject)) { return 0; } const FIntProperty* IntProp = CastField(TargetObject->GetClass()->FindPropertyByName(PropertyName)); if(IntProp == nullptr) { return 0; } bHasProperty = true; return IntProp->GetPropertyValue(IntProp->ContainerPtrToValuePtr(TargetObject)); } void UGDMPropertyWidget::SetPropertyValue_Int(int32 NewValue, bool& bHasProperty) { bHasProperty = false; if(!IsValid(TargetObject)) { return; } const FIntProperty* IntProp = CastField(TargetObject->GetClass()->FindPropertyByName(PropertyName)); if(IntProp == nullptr) { return; } bHasProperty = true; const int32 OldValue = IntProp->GetPropertyValue(IntProp->ContainerPtrToValuePtr(TargetObject)); if (NewValue != OldValue) { IntProp->SetPropertyValue(IntProp->ContainerPtrToValuePtr(TargetObject), NewValue); UGameDebugMenuFunctions::GetGameDebugMenuManager(this)->CallChangePropertyIntDispatcher(PropertyName, TargetObject, NewValue, OldValue, PropertySaveKey); } } uint8 UGDMPropertyWidget::GetPropertyValue_Byte(bool& bHasProperty) { bHasProperty = false; if(!IsValid(TargetObject)) { return 0; } const FProperty* Prop = TargetObject->GetClass()->FindPropertyByName(PropertyName); if( Prop == nullptr ) { return 0; } const FEnumProperty* EnumProp = CastField(Prop); if( EnumProp != nullptr ) { bHasProperty = true; const FNumericProperty* NumProp = EnumProp->GetUnderlyingProperty(); const uint64 Value = NumProp->GetUnsignedIntPropertyValue(EnumProp->ContainerPtrToValuePtr(TargetObject)); return static_cast( Value ); } const FByteProperty* ByteProp = CastField(Prop); if( ByteProp != nullptr ) { bHasProperty = true; return ByteProp->GetPropertyValue(ByteProp->ContainerPtrToValuePtr(TargetObject)); } return 0; } void UGDMPropertyWidget::SetPropertyValue_Byte(uint8 NewValue, bool& bHasProperty) { bHasProperty = false; if(!IsValid(TargetObject)) { return; } const FProperty* Prop = TargetObject->GetClass()->FindPropertyByName(PropertyName); if( Prop == nullptr ) { return; } const FEnumProperty* EnumProp = CastField(Prop); if( EnumProp != nullptr ) { bHasProperty = true; const FNumericProperty* NumProp = EnumProp->GetUnderlyingProperty(); const uint8 OldValue = static_cast( NumProp->GetUnsignedIntPropertyValue(EnumProp->ContainerPtrToValuePtr(TargetObject)) ); if( NewValue != OldValue ) { NumProp->SetIntPropertyValue(EnumProp->ContainerPtrToValuePtr(TargetObject),static_cast( NewValue )); UGameDebugMenuFunctions::GetGameDebugMenuManager(this)->CallChangePropertyByteDispatcher(PropertyName,TargetObject,NewValue,OldValue, PropertySaveKey); } return; } const FByteProperty* ByteProp = CastField(Prop); if( ByteProp != nullptr ) { bHasProperty = true; const uint8 OldValue = ByteProp->GetPropertyValue(ByteProp->ContainerPtrToValuePtr(TargetObject)); if( NewValue != OldValue ) { ByteProp->SetPropertyValue(ByteProp->ContainerPtrToValuePtr(TargetObject),NewValue); UGameDebugMenuFunctions::GetGameDebugMenuManager(this)->CallChangePropertyByteDispatcher(PropertyName, TargetObject, NewValue, OldValue, PropertySaveKey); } } } TArray UGDMPropertyWidget::GetEnumDisplayNames(const FString& EnumPath, bool& bHasProperty) { TArray Result; const UEnum* Enum = FindObject(nullptr, *EnumPath); if(!IsValid(Enum)) { return Result; } const int32 Num = Enum->NumEnums() - 1; for(int32 Index = 0;Index < Num; ++Index) { Result.Add(Enum->GetDisplayNameTextByIndex(Index)); } bHasProperty = true; return Result; } FString UGDMPropertyWidget::GetPropertyValue_String(bool& bHasProperty) { bHasProperty = false; if(!IsValid(TargetObject)) { return FString(); } const FStrProperty* StrProp = CastField(TargetObject->GetClass()->FindPropertyByName(PropertyName)); if (StrProp == nullptr) { return FString(); } bHasProperty = true; return StrProp->GetPropertyValue(StrProp->ContainerPtrToValuePtr(TargetObject)); } void UGDMPropertyWidget::SetPropertyValue_String(FString NewValue, bool& bHasProperty) { bHasProperty = false; if(!IsValid(TargetObject)) { return; } const FStrProperty* StrProp = CastField(TargetObject->GetClass()->FindPropertyByName(PropertyName)); if (StrProp == nullptr) { return; } bHasProperty = true; const FString OldValue = StrProp->GetPropertyValue(StrProp->ContainerPtrToValuePtr(TargetObject)); if (NewValue != OldValue) { StrProp->SetPropertyValue(StrProp->ContainerPtrToValuePtr(TargetObject), NewValue); UGameDebugMenuFunctions::GetGameDebugMenuManager(this)->CallChangePropertyStringDispatcher(PropertyName, TargetObject, NewValue, OldValue, PropertySaveKey); } } FVector UGDMPropertyWidget::GetPropertyValue_Vector(bool& bHasProperty) { bHasProperty = false; if( !IsValid(TargetObject) ) { return FVector::ZeroVector; } const FStructProperty* StructProp = CastField(TargetObject->GetClass()->FindPropertyByName(PropertyName)); if( StructProp == nullptr ) { return FVector::ZeroVector; } const UStruct* Struct = StructProp->Struct; if( Struct == nullptr ) { return FVector::ZeroVector; } if( Struct->IsChildOf(TBaseStructure::Get()) ) { if(const FVector* Value = StructProp->ContainerPtrToValuePtr(TargetObject) ) { bHasProperty = true; return (*Value); } } return FVector::ZeroVector; } void UGDMPropertyWidget::SetPropertyValue_Vector(FVector NewValue, bool& bHasProperty) { bHasProperty = false; if( !IsValid(TargetObject) ) { return; } const FStructProperty* StructProp = CastField(TargetObject->GetClass()->FindPropertyByName(PropertyName)); if( StructProp == nullptr ) { return; } const UStruct* Struct = StructProp->Struct; if( Struct == nullptr ) { return; } if( Struct->IsChildOf(TBaseStructure::Get()) ) { if( FVector* Value = StructProp->ContainerPtrToValuePtr(TargetObject) ) { const FVector OldValue = *Value; bHasProperty = true; if( NewValue != OldValue ) { *Value = NewValue; UGameDebugMenuFunctions::GetGameDebugMenuManager(this)->CallChangePropertyVectorDispatcher(PropertyName, TargetObject, NewValue, OldValue, PropertySaveKey); } } } } FVector2D UGDMPropertyWidget::GetPropertyValue_Vector2D(bool& bHasProperty) { bHasProperty = false; if( !IsValid(TargetObject) ) { return FVector2D::ZeroVector; } const FStructProperty* StructProp = CastField(TargetObject->GetClass()->FindPropertyByName(PropertyName)); if( StructProp == nullptr ) { return FVector2D::ZeroVector; } const UStruct* Struct = StructProp->Struct; if( Struct == nullptr ) { return FVector2D::ZeroVector; } if( Struct->IsChildOf(TBaseStructure::Get()) ) { if(const FVector2D* Value = StructProp->ContainerPtrToValuePtr(TargetObject) ) { bHasProperty = true; return (*Value); } } return FVector2D::ZeroVector; } void UGDMPropertyWidget::SetPropertyValue_Vector2D(FVector2D NewValue, bool& bHasProperty) { bHasProperty = false; if( !IsValid(TargetObject) ) { return; } const FStructProperty* StructProp = CastField(TargetObject->GetClass()->FindPropertyByName(PropertyName)); if( StructProp == nullptr ) { return; } const UStruct* Struct = StructProp->Struct; if( Struct == nullptr ) { return; } if( Struct->IsChildOf(TBaseStructure::Get()) ) { if( FVector2D* Value = StructProp->ContainerPtrToValuePtr(TargetObject) ) { const FVector2D OldValue = *Value; bHasProperty = true; if( NewValue != OldValue ) { *Value = NewValue; UGameDebugMenuFunctions::GetGameDebugMenuManager(this)->CallChangePropertyVector2DDispatcher(PropertyName, TargetObject, NewValue, OldValue, PropertySaveKey); } } } } FRotator UGDMPropertyWidget::GetPropertyValue_Rotator(bool& bHasProperty) { bHasProperty = false; if( !IsValid(TargetObject) ) { return FRotator::ZeroRotator; } const FStructProperty* StructProp = CastField(TargetObject->GetClass()->FindPropertyByName(PropertyName)); if( StructProp == nullptr ) { return FRotator::ZeroRotator; } const UStruct* Struct = StructProp->Struct; if( Struct == nullptr ) { return FRotator::ZeroRotator; } if( Struct->IsChildOf(TBaseStructure::Get()) ) { if(const FRotator* Value = StructProp->ContainerPtrToValuePtr(TargetObject) ) { bHasProperty = true; return (*Value); } } return FRotator::ZeroRotator; } void UGDMPropertyWidget::SetPropertyValue_Rotator(FRotator NewValue, bool& bHasProperty) { bHasProperty = false; if( !IsValid(TargetObject) ) { return; } const FStructProperty* StructProp = CastField(TargetObject->GetClass()->FindPropertyByName(PropertyName)); if( StructProp == nullptr ) { return; } const UStruct* Struct = StructProp->Struct; if( Struct == nullptr ) { return; } if( Struct->IsChildOf(TBaseStructure::Get()) ) { if( FRotator* Value = StructProp->ContainerPtrToValuePtr(TargetObject) ) { const FRotator OldValue = *Value; bHasProperty = true; if( NewValue != OldValue ) { *Value = NewValue; UGameDebugMenuFunctions::GetGameDebugMenuManager(this)->CallChangePropertyRotatorDispatcher(PropertyName, TargetObject, NewValue, OldValue, PropertySaveKey); } } } } ``` `SampleProject/Plugins/GameDebugMenu/Source/GameDebugMenu/Private/Widgets/GDMTextBlock.cpp`: ```cpp /** * Copyright (c) 2020 akihiko moroi * * This software is released under the MIT License. * (See accompanying file LICENSE.txt or copy at http://opensource.org/licenses/MIT) */ #include "Widgets/GDMTextBlock.h" #include "GameDebugMenuFunctions.h" #include "GameDebugMenuSettings.h" #include "GameDebugMenuManager.h" #include "Component/GDMLocalizeStringComponent.h" #include "Engine/Engine.h" #include "Widgets/GameDebugMenuRootWidget.h" #include "Widgets/Text/STextBlock.h" #define LOCTEXT_NAMESPACE "GameDebugMenu" UGDMTextBlock::UGDMTextBlock(const FObjectInitializer& ObjectInitializer) : Super(ObjectInitializer) , DebugMenuStringKey() #if WITH_EDITORONLY_DATA , PreviewLanguageKey(TEXT("Japanese")) #endif { if( !IsRunningDedicatedServer() ) { SetFont(FSlateFontInfo(GetDefault()->GetDebugMenuFont(), 24, FName("Bold"))); } } void UGDMTextBlock::SynchronizeProperties() { Super::SynchronizeProperties(); if( !DebugMenuStringKey.IsEmpty() ) { SetText(FText::FromString(DebugMenuStringKey)); } } void UGDMTextBlock::SetText(FText InText) { FString StringKey = InText.ToString(); if( DebugMenuStringKey != StringKey ) { DebugMenuStringKey.Reset(); } UWorld* World = GetWorld(); if( !IsValid(World) ) { if( UGameDebugMenuRootWidget* RootWidget = Cast(GetOuter()) ) { World = RootWidget->GetOwnerManager()->GetWorld(); } else { World = GEngine->GetWorldFromContextObject(World, EGetWorldErrorMode::LogAndReturnNull); } } FString DebugMenuStr; #if WITH_EDITORONLY_DATA if( !InText.IsEmpty() ) { if( IsValid(World) ) { /* テキストのStringKeyなら取得しセットするテキストを上書きする */ if( World->WorldType == EWorldType::Editor || World->WorldType == EWorldType::EditorPreview ) { DebugMenuStr = GetDefault()->GetDebugMenuString(PreviewLanguageKey, StringKey); if( !DebugMenuStr.IsEmpty() ) { DebugMenuStringKey = InText.ToString(); InText = FText::FromString(DebugMenuStr); } } else if( AGameDebugMenuManager* Manager = UGameDebugMenuFunctions::GetGameDebugMenuManager(World) ) { if( Manager->GetLocalizeStringComponent()->GetString(StringKey, DebugMenuStr) ) { DebugMenuStringKey = InText.ToString(); InText = FText::FromString(DebugMenuStr); } } } } #else if( !InText.IsEmpty() ) { if( UGameDebugMenuFunctions::GetDebugMenuString(World, StringKey, DebugMenuStr) ) { DebugMenuStringKey = InText.ToString(); InText = FText::FromString(DebugMenuStr); } } #endif Super::SetText(InText); } void UGDMTextBlock::SetWrapTextAt(float InWrapTextAt) { WrapTextAt = InWrapTextAt; if( MyTextBlock.IsValid() ) { MyTextBlock->SetWrapTextAt(InWrapTextAt); } } #if WITH_EDITOR const FText UGDMTextBlock::GetPaletteCategory() { return LOCTEXT("GDM", "GDM"); } bool UGDMTextBlock::CanEditChange(const FProperty* InProperty) const { return Super::CanEditChange(InProperty); } #endif #undef LOCTEXT_NAMESPACE ``` `SampleProject/Plugins/GameDebugMenu/Source/GameDebugMenu/Private/Widgets/GameDebugMenuRootWidget.cpp`: ```cpp /** * Copyright (c) 2020 akihiko moroi * * This software is released under the MIT License. * (See accompanying file LICENSE.txt or copy at http://opensource.org/licenses/MIT) */ #include "Widgets/GameDebugMenuRootWidget.h" #include "GameDebugMenuManager.h" #include "Input/GDMInputSystemComponent.h" void UGameDebugMenuRootWidget::NativeConstruct() { Super::NativeConstruct(); /* ルートは常にアクティブ状態に */ bActivateMenu = true; } void UGameDebugMenuRootWidget::NativeDestruct() { Super::NativeDestruct(); } void UGameDebugMenuRootWidget::ActivateDebugMenu() { /* ルートは使用しない */ } void UGameDebugMenuRootWidget::DeactivateDebugMenu() { /* ルートは使用しない */ } void UGameDebugMenuRootWidget::SetDebugMenuManager(AGameDebugMenuManager* InManager) { Manager = InManager; } AGameDebugMenuManager* UGameDebugMenuRootWidget::GetOwnerManager() const { return Manager; } void UGameDebugMenuRootWidget::SwitchInputComponentGroupForGameDebugMenu(const FName NewGroupName) { Manager->GetDebugMenuInputSystemComponent()->SwitchToInputGroup(NewGroupName); } ``` `SampleProject/Plugins/GameDebugMenu/Source/GameDebugMenu/Private/Widgets/GameDebugMenuWidget.cpp`: ```cpp /** * Copyright (c) 2020 akihiko moroi * * This software is released under the MIT License. * (See accompanying file LICENSE.txt or copy at http://opensource.org/licenses/MIT) */ #include "Widgets/GameDebugMenuWidget.h" #include #include "Engine/DebugCameraController.h" #include "GameDebugMenuManager.h" #include "Component/GDMPlayerControllerProxyComponent.h" #include "GameDebugMenuFunctions.h" #include "GameDebugMenuSettings.h" #include "Input/GDMEnhancedInputComponent.h" UGameDebugMenuWidget::UGameDebugMenuWidget(const FObjectInitializer& ObjectInitializer) : Super(ObjectInitializer) , OnSendWidgetEventDispatcher() , bActivateMenu(false) , InputHandles() { /* UE5.7+: UUserWidgetはBPの入力ノード有無に応じてInputComponentを自動生成/登録し得る。 * GameDebugMenuはUGDMInputSystemComponentでPush/Popを自前管理するため、自動登録は無効化しておく*/ #if ENGINE_MAJOR_VERSION > 5 || (ENGINE_MAJOR_VERSION == 5 && ENGINE_MINOR_VERSION >= 7) bAutomaticallyRegisterInputOnConstruction = false; #endif } void UGameDebugMenuWidget::CreateInputComponent() { /* 手動で生成するので何もしない */ } void UGameDebugMenuWidget::StartProcessingInputScriptDelegates() { /* 何もしない(意図的) * GameDebugMenuはUGDMInputSystemComponentで入力スタックを自前管理する。 * ここでSuperを呼ぶとUUserWidgetがInputComponentを勝手にPushして、bBlockInput等でゲーム入力を塞ぎ得る。*/ } void UGameDebugMenuWidget::StopProcessingInputScriptDelegates() { /* 何もしない(意図的) * StartProcessingInputScriptDelegates() と対で、UUserWidgetの自動Pop/解除経路が * プラグイン管理の入力スタックと干渉しないようにする。*/ } void UGameDebugMenuWidget::EnsureDebugMenuInputComponent() { if (IsValid(InputComponent)) { return; } if ( APlayerController* Controller = GetOwningPlayer() ) { const UClass* Class = GetDefault()->GetDebugMenuInputComponentClass(); InputComponent = NewObject( Controller, Class, NAME_None, RF_Transient); } } APlayerController* UGameDebugMenuWidget::GetOriginalPlayerController() const { APlayerController* PlayerController = GetOwningPlayer(); const ADebugCameraController* DCC = Cast(PlayerController); if (IsValid(DCC)) { /* FLocalPlayerContextのGetPlayerControllerはADebugCameraControllerを無視できないのでオリジナルを返す */ return DCC->OriginalControllerRef; } return PlayerController; } UGDMEnhancedInputComponent* UGameDebugMenuWidget::GetMyInputComponent() const { return Cast(InputComponent); } bool UGameDebugMenuWidget::RegisterDebugMenuWidgetInputFunction(const UInputAction* Action, const FName FunctionName, const ETriggerEvent TriggerEvent, UObject* FunctionObject) { EnsureDebugMenuInputComponent(); if (UGDMEnhancedInputComponent* InputComp = GetMyInputComponent()) { if (!IsValid(FunctionObject)) { FunctionObject = this; } InputHandles.Add(InputComp->BindDebugMenuAction(Action, TriggerEvent, FunctionObject, FunctionName).GetHandle()); return true; } return false; } bool UGameDebugMenuWidget::RegisterDebugMenuWidgetInputEvent(const UInputAction* Action, FOnGameDebugMenuWidgetInputAction Callback, const ETriggerEvent TriggerEvent) { EnsureDebugMenuInputComponent(); if (UGDMEnhancedInputComponent* InputComp = GetMyInputComponent()) { const FEnhancedInputActionEventBinding& Binding = InputComp->BindDebugMenuAction( Action, TriggerEvent, [WeakThis = TWeakObjectPtr(this), Callback](const FInputActionInstance& Instance) { if (WeakThis.IsValid() && Callback.IsBound()) { Callback.Execute(); } }); InputHandles.Add(Binding.GetHandle()); return true; } return false; } void UGameDebugMenuWidget::UnregisterDebugMenuWidgetInputs() { if (UGDMEnhancedInputComponent* InputComp = GetMyInputComponent()) { for (const auto Handle : InputHandles) { InputComp->RemoveBindingByHandle(Handle); } InputHandles.Reset(); } } void UGameDebugMenuWidget::SendSelfEvent(FName EventName) { OnSendWidgetEventDispatcher.Broadcast(this, EventName); } void UGameDebugMenuWidget::ExecuteGDMConsoleCommand(const FString Command, const EGDMConsoleCommandNetType CommandNetType) { if (const APlayerController* PC = GetOriginalPlayerController() ) { if( UGDMPlayerControllerProxyComponent* DebugMenuPCProxyComponent = PC->FindComponentByClass() ) { DebugMenuPCProxyComponent->ExecuteConsoleCommand(Command, CommandNetType); } } } bool UGameDebugMenuWidget::IsActivateDebugMenu() { return bActivateMenu; } void UGameDebugMenuWidget::ActivateDebugMenu() { if (bActivateMenu) { return; } bActivateMenu = true; UGameDebugMenuFunctions::RegisterInputComponentForGameDebugMenu(this, InputComponent); OnActivateDebugMenu(); } void UGameDebugMenuWidget::DeactivateDebugMenu() { if (!bActivateMenu) { return; } bActivateMenu = false; UGameDebugMenuFunctions::UnregisterInputComponentForGameDebugMenu(this, InputComponent); OnDeactivateDebugMenu(); } void UGameDebugMenuWidget::OnChangeDebugMenuLanguage(const FName& NewLanguageKey, const FName& OldLanguageKey) { OnChangeDebugMenuLanguageBP(NewLanguageKey, OldLanguageKey); } bool UGameDebugMenuWidget::GetWidgetChildrenOfClass(TSubclassOf WidgetClass, TArray& OutChildWidgets, bool bEndSearchAsYouFind) { OutChildWidgets.Reset(); /* 現在チェックするWidget郡 */ TInlineComponentArray WidgetsToCheck; /* チェック済みWidget郡 */ TInlineComponentArray CheckedWidgets; /* 作業用 */ TArray WorkWidgets; WidgetsToCheck.Push(this); while( WidgetsToCheck.Num() > 0 ) { UWidget* PossibleParent = WidgetsToCheck.Pop(EAllowShrinking::No); if( CheckedWidgets.Contains(PossibleParent) ) { /* チェック済み */ continue; } CheckedWidgets.Add(PossibleParent); WorkWidgets.Reset(); if(const UUserWidget* UserWidget = Cast(PossibleParent) ) { if( UserWidget->WidgetTree != nullptr ) { UserWidget->WidgetTree->GetAllWidgets(WorkWidgets); } } else { UWidgetTree::GetChildWidgets(PossibleParent, WorkWidgets); } for( UWidget* Widget : WorkWidgets ) { if( CheckedWidgets.Contains(Widget) ) { /* チェック済み */ continue; } if( Widget->GetClass()->IsChildOf(WidgetClass) ) { OutChildWidgets.Add(Widget); if( bEndSearchAsYouFind ) { /* 一致したものがあればそのまま終了 */ return (OutChildWidgets.Num() > 0); } } WidgetsToCheck.Push(Widget); } } return (OutChildWidgets.Num() > 0); } ``` `SampleProject/Plugins/GameDebugMenu/Source/GameDebugMenu/Public/Component/GDMListenerComponent.h`: ```h /** * Copyright (c) 2020 akihiko moroi * * This software is released under the MIT License. * (See accompanying file LICENSE.txt or copy at http://opensource.org/licenses/MIT) */ #pragma once #include "CoreMinimal.h" #include "GDMListenerComponent.generated.h" DECLARE_DYNAMIC_MULTICAST_DELEGATE(FGDMGameDebugMenuListenerDelegate); DECLARE_DYNAMIC_MULTICAST_DELEGATE_OneParam(FGDMOnExecuteConsoleCommandDelegate, const FString&, Command); DECLARE_DYNAMIC_MULTICAST_DELEGATE_TwoParams(FGDMOnExecuteProcessEventDelegate, const FName&, FunctionName, UObject*, FunctionOwnerObject); DECLARE_DYNAMIC_MULTICAST_DELEGATE_FiveParams(FGDMOnChangePropertyBoolDelegate, const FName&, PropertyName, UObject*, PropertyOwnerObject, bool, New, bool, Old, const FString&, PropertySaveKey); DECLARE_DYNAMIC_MULTICAST_DELEGATE_FiveParams(FGDMOnChangePropertyIntDelegate, const FName&, PropertyName, UObject*, PropertyOwnerObject, int32, New, int32, Old, const FString&, PropertySaveKey); DECLARE_DYNAMIC_MULTICAST_DELEGATE_FiveParams(FGDMOnChangePropertyFloatDelegate, const FName&, PropertyName, UObject*, PropertyOwnerObject, float, New, float, Old, const FString&, PropertySaveKey); DECLARE_DYNAMIC_MULTICAST_DELEGATE_FiveParams(FGDMOnChangePropertyByteDelegate, const FName&, PropertyName, UObject*, PropertyOwnerObject, uint8, New, uint8, Old, const FString&, PropertySaveKey); DECLARE_DYNAMIC_MULTICAST_DELEGATE_FiveParams(FGDMOnChangePropertyStringDelegate, const FName&, PropertyName, UObject*, PropertyOwnerObject, FString, New, FString, Old, const FString&, PropertySaveKey); DECLARE_DYNAMIC_MULTICAST_DELEGATE_FiveParams(FGDMOnChangePropertyVectorDelegate, const FName&, PropertyName, UObject*, PropertyOwnerObject, FVector, New, FVector, Old, const FString&, PropertySaveKey); DECLARE_DYNAMIC_MULTICAST_DELEGATE_FiveParams(FGDMOnChangePropertyVector2DDelegate, const FName&, PropertyName, UObject*, PropertyOwnerObject, FVector2D, New, FVector2D, Old, const FString&, PropertySaveKey); DECLARE_DYNAMIC_MULTICAST_DELEGATE_FiveParams(FGDMOnChangePropertyRotatorDelegate, const FName&, PropertyName, UObject*, PropertyOwnerObject, FRotator, New, FRotator, Old, const FString&, PropertySaveKey); DECLARE_DYNAMIC_MULTICAST_DELEGATE_OneParam(FGDMOnInputSystemDelegate, UObject*, TargetInputObject); DECLARE_DYNAMIC_MULTICAST_DELEGATE_TwoParams(FGDMOnInputSystemChangeInputObjectDelegate, UObject*, NewInputObject, UObject*, OldInputObject); DECLARE_DYNAMIC_MULTICAST_DELEGATE_TwoParams(FGDMOnChangeDebugMenuLanguageDelegate, const FName&, NewLanguageKey, const FName&, OldLanguageKey); /** * DebugMenuでのイベントを取得できるコンポーネント */ UCLASS(Blueprintable, ClassGroup = (GameDebugMenu), hidecategories = Object, meta = (BlueprintSpawnableComponent)) class GAMEDEBUGMENU_API UGDMListenerComponent : public UActorComponent { GENERATED_BODY() public: /** DebugMenuが表示されたとき呼ばれるイベント */ UPROPERTY(BlueprintAssignable, Category = "GDM|Dispatcher") FGDMGameDebugMenuListenerDelegate OnShowDispatcher; /** DebugMenuが閉じたとき呼ばれるイベント */ UPROPERTY(BlueprintAssignable, Category = "GDM|Dispatcher") FGDMGameDebugMenuListenerDelegate OnHideDispatcher; /** DebugMenuからコンソールコマンド実行した時に呼ばれるイベント */ UPROPERTY(BlueprintAssignable, Category = "GDM|Dispatcher") FGDMOnExecuteConsoleCommandDelegate OnExecuteConsoleCommandDispatcher; /** DebugMenuに登録された関数が実行されたとき呼ばれるイベント */ UPROPERTY(BlueprintAssignable, Category = "GDM|Dispatcher") FGDMOnExecuteProcessEventDelegate OnExecuteProcessEventDispatcher; /** DebugMenuに登録されたプロパティ(Bool)が変更されたとき呼ばれるイベント */ UPROPERTY(BlueprintAssignable, Category = "GDM|Dispatcher") FGDMOnChangePropertyBoolDelegate OnChangePropertyBoolDispatcher; /** DebugMenuに登録されたプロパティ(Int)が変更されたとき呼ばれるイベント */ UPROPERTY(BlueprintAssignable, Category = "GDM|Dispatcher") FGDMOnChangePropertyIntDelegate OnChangePropertyIntDispatcher; /** DebugMenuに登録されたプロパティ(Float)が変更されたとき呼ばれるイベント */ UPROPERTY(BlueprintAssignable, Category = "GDM|Dispatcher") FGDMOnChangePropertyFloatDelegate OnChangePropertyFloatDispatcher; /** DebugMenuに登録されたプロパティ(Byte)が変更されたとき呼ばれるイベント */ UPROPERTY(BlueprintAssignable, Category = "GDM|Dispatcher") FGDMOnChangePropertyByteDelegate OnChangePropertyByteDispatcher; /** DebugMenuに登録されたプロパティ(String)が変更されたとき呼ばれるイベント */ UPROPERTY(BlueprintAssignable, Category = "GDM|Dispatcher") FGDMOnChangePropertyStringDelegate OnChangePropertyStringDispatcher; /** DebugMenuに登録されたプロパティ(Vector)が変更されたとき呼ばれるイベント */ UPROPERTY(BlueprintAssignable, Category = "GDM|Dispatcher") FGDMOnChangePropertyVectorDelegate OnChangePropertyVectorDispatcher; /** DebugMenuに登録されたプロパティ(Vector2D)が変更されたとき呼ばれるイベント */ UPROPERTY(BlueprintAssignable, Category = "GDM|Dispatcher") FGDMOnChangePropertyVector2DDelegate OnChangePropertyVector2DDispatcher; /** DebugMenuに登録されたプロパティ(Rotator)が変更されたとき呼ばれるイベント */ UPROPERTY(BlueprintAssignable, Category = "GDM|Dispatcher") FGDMOnChangePropertyRotatorDelegate OnChangePropertyRotatorDispatcher; /** DebugMenuの使用言語が変更されたときに呼ばれるイベント */ UPROPERTY(BlueprintAssignable, Category = "GDM|Dispatcher") FGDMOnChangeDebugMenuLanguageDelegate OnChangeDebugMenuLanguageDispatcher; /** DebugMenuでスクショ処理の開始時に呼ばれるイベント */ UPROPERTY(BlueprintAssignable, Category = "GDM|Dispatcher") FGDMGameDebugMenuListenerDelegate OnStartScreenshotRequestDispatcher; /** DebugMenuでスクショ処理の終了時に呼ばれるイベント */ UPROPERTY(BlueprintAssignable, Category = "GDM|Dispatcher") FGDMGameDebugMenuListenerDelegate OnScreenshotRequestProcessedDispatcher; /** DebugMenuでロードが完了した時に呼ばれるイベント */ UPROPERTY(BlueprintAssignable, Category = "GDM|Dispatcher") FGDMGameDebugMenuListenerDelegate OnLoadedDebugMenuDispatcher; /** DebugMenuでセーブが完了した時に呼ばれるイベント */ UPROPERTY(BlueprintAssignable, Category = "GDM|Dispatcher") FGDMGameDebugMenuListenerDelegate OnSavedDebugMenuDispatcher; /** DebugMenuのセーブが削除した時に呼ばれるイベント */ UPROPERTY(BlueprintAssignable, Category = "GDM|Dispatcher") FGDMGameDebugMenuListenerDelegate OnDeletedDebugMenuDispatcher; public: UGDMListenerComponent(const FObjectInitializer& ObjectInitializer = FObjectInitializer::Get()); virtual void InitializeComponent() override; virtual void UninitializeComponent() override; public: UFUNCTION(BlueprintCallable) void AllUnbindDispatchers(); public: static int32 PushListenerComponent(UWorld* TargetWorld, UGDMListenerComponent* Listener); static int32 PopListenerComponent(UWorld* TargetWorld, UGDMListenerComponent* Listener); static void GetAllListenerComponents(UWorld* TargetWorld, TArray& OutListenerComponents); }; ``` `SampleProject/Plugins/GameDebugMenu/Source/GameDebugMenu/Public/Component/GDMLocalizeStringComponent.h`: ```h /** * Copyright (c) 2020 akihiko moroi * * This software is released under the MIT License. * (See accompanying file LICENSE.txt or copy at http://opensource.org/licenses/MIT) */ #pragma once #include "CoreMinimal.h" #include "GameDebugMenuTypes.h" #include "Components/ActorComponent.h" #include "GDMLocalizeStringComponent.generated.h" class UGDMPropertyJsonSystemComponent; /** * DebugMenuの文字列管理 */ UCLASS(NotBlueprintable, NotBlueprintType) class GAMEDEBUGMENU_API UGDMLocalizeStringComponent : public UActorComponent { GENERATED_BODY() /** 読み込んだ文字のキーと現在の言語にあった文字列 */ UPROPERTY(Transient) TMap CachedDebugMenuStrings; /** True: デバックメニュー用の StringKey を指定してる箇所をそのまま表示する */ UPROPERTY(Transient) bool bCurrentDebugMenuDirectStringKey; /** 現在のデバックメニューの言語 */ UPROPERTY(Transient) FName CurrentLanguage; public: UGDMLocalizeStringComponent(); public: /** * Json情報を取得しセットする */ void SetJsonSystemComponentValue(UGDMPropertyJsonSystemComponent* PropertyJsonSystemComponent); /** * Json情報を取得しセットする */ void SetToJsonSystemComponent(UGDMPropertyJsonSystemComponent* PropertyJsonSystemComponent, const FString& Language); /** * DebugMenu用のローカライズされた文字列を返す */ bool GetString(const FString& StringKey, FString& OutString); void SyncLoadDebugMenuStringTables(); const FName& GetCurrentDebugMenuLanguage() const; }; /************************************************************************************************************************************/ inline bool UGDMLocalizeStringComponent::GetString(const FString& StringKey, FString& OutString) { if( FString* SourceString = CachedDebugMenuStrings.Find(StringKey) ) { if( bCurrentDebugMenuDirectStringKey ) { /* 取得できたキーをそのまま戻す */ OutString = StringKey; } else { OutString = *SourceString; } } else { OutString = StringKey; UE_LOG(LogGDM, Verbose, TEXT("GetDebugMenuString: Not found StringKey %s"), *StringKey); return false; } return true; } inline const FName& UGDMLocalizeStringComponent::GetCurrentDebugMenuLanguage() const { return CurrentLanguage; } ``` `SampleProject/Plugins/GameDebugMenu/Source/GameDebugMenu/Public/Component/GDMPlayerControllerProxyComponent.h`: ```h /** * Copyright (c) 2020 akihiko moroi * * This software is released under the MIT License. * (See accompanying file LICENSE.txt or copy at http://opensource.org/licenses/MIT) */ #pragma once #include "CoreMinimal.h" #include "GameDebugMenuTypes.h" #include "GDMPlayerControllerProxyComponent.generated.h" class AGameDebugMenuManager; /** * DebugMenuManager生成時、PlayerControllerに自動で追加されるコンポーネント * 使用用途としてはマルチプレイなどでDebug目的の通信が必要な場合、PlayerControllerの代わりに記述などができる */ UCLASS(Blueprintable, NotBlueprintType, noteditinlinenew) class GAMEDEBUGMENU_API UGDMPlayerControllerProxyComponent : public UActorComponent { GENERATED_BODY() public: UPROPERTY(Replicated) TObjectPtr DebugMenuManager = nullptr; public: UGDMPlayerControllerProxyComponent(); virtual void GetLifetimeReplicatedProps(TArray& OutLifetimeProps ) const override; virtual void BeginPlay() override; virtual void EndPlay(const EEndPlayReason::Type EndPlayReason) override; public: UFUNCTION(BlueprintPure) APlayerController* GetOwnerPlayerController() const; UFUNCTION(BlueprintPure) APawn* GetOwnerPlayerPawn() const; UFUNCTION(BlueprintPure) ACharacter* GetOwnerPlayerCharacter() const; UFUNCTION(BlueprintPure) AGameDebugMenuManager* GetDebugMenuManager() const; UFUNCTION(Server, Reliable, WithValidation) void ROS_ExecuteConsoleCommand(const FString& Command,bool bAllClient); UFUNCTION(Client, Reliable, WithValidation) void ROC_ExecuteConsoleCommand(const FString& Command); virtual void ExecuteConsoleCommand(const FString& Command, EGDMConsoleCommandNetType CommandNetType); virtual void AllExecuteConsoleCommand_Server(const FString& Command); }; ``` `SampleProject/Plugins/GameDebugMenu/Source/GameDebugMenu/Public/Component/GDMPropertyJsonSystemComponent.h`: ```h /** * Copyright (c) 2020 akihiko moroi * * This software is released under the MIT License. * (See accompanying file LICENSE.txt or copy at http://opensource.org/licenses/MIT) */ #pragma once #include "CoreMinimal.h" #include "GameDebugMenuTypes.h" #include "Components/ActorComponent.h" #include "GDMPropertyJsonSystemComponent.generated.h" /** * DebugMenu全体で管理するJsonへの読み書きを管理するコンポーネント */ UCLASS(NotBlueprintable, NotBlueprintType) class GAMEDEBUGMENU_API UGDMPropertyJsonSystemComponent : public UActorComponent { GENERATED_BODY() private: static const FString JsonField_RootProperty; static const FString JsonField_RootFunction; static const FString JsonField_RootCustom; static const FString JsonField_RootFavorite; static const FString JsonField_FavoriteDefinitionName; static const FString JsonField_FavoriteSaveKey; TSharedPtr RootJsonObject; public: UGDMPropertyJsonSystemComponent(const FObjectInitializer& ObjectInitializer = FObjectInitializer::Get()); virtual void BeginPlay() override; public: /** * 対象のオブジェクトのプロパティをJsonに追加する */ UFUNCTION(BlueprintCallable) void AddPropertyToJson(const FString& ObjectKey, UObject* TargetObject, const FString& PropertyName) const; /** * Jsonから指定プロパティを削除する */ UFUNCTION(BlueprintCallable) void RemovePropertyFromJson(const FString& ObjectKey, const FString& PropertyName) const; /** * Json内のプロパティ情報を対象オブジェクトに反映する * @return true: 反映に成功 false: データがなかったか取得に失敗した */ bool ApplyJsonToObjectProperty(const FString& ObjectKey, UObject* TargetObject, const FString& PropertyName) const; UFUNCTION(BlueprintCallable) void AddFunctionToJson(const FString& ObjectKey, UObject* TargetObject, const FString& FunctionName) const; UFUNCTION(BlueprintCallable) void RemoveFunctionFromJson(const FString& ObjectKey, const FString& FunctionName) const; bool HaveFunctionInJson(const FString& ObjectKey, UObject* TargetObject, const FString& FunctionName) const; /** * お気に入り情報を追加 */ UFUNCTION(BlueprintCallable) void AddFavoriteEntry(const FString& DefinitionName, const FString& FavoriteSaveKey); /** * お気に入り情報を削除 */ UFUNCTION(BlueprintCallable) bool RemoveFavoriteEntry(const FString& DefinitionName, const FString& FavoriteSaveKey); /** * お気に入り情報が存在するか? */ UFUNCTION(BlueprintCallable) bool HasFavoriteEntry(const FString& DefinitionName, const FString& FavoriteSaveKey) const; /** * すべてのお気に入り情報を取得する */ UFUNCTION(BlueprintCallable) TArray GetAllFavoriteEntries() const; /** * 配列の文字列をJsonにセットする */ UFUNCTION(BlueprintCallable) void SetCustomStringArray(const FString& Key, const TArray& StringArray); /** * 単一の文字列をJsonにセットする */ UFUNCTION(BlueprintCallable) void SetCustomString(const FString& Key, const FString& StringValue); /** * 配列の文字列をJsonから取得する * @param Key */ UFUNCTION(BlueprintCallable) TArray GetCustomStringArray(const FString& Key) const; /** * 単一の文字列をJsonから取得 */ UFUNCTION(BlueprintCallable) FString GetCustomString(const FString& Key, const FString& DefaultValue = TEXT("")) const; /** * Json内に指定されたキーで文字列が存在するかを確認する */ UFUNCTION(BlueprintCallable) bool HasCustomString(const FString& Key) const; /** * Jsonを文字列で取得 */ UFUNCTION(BlueprintCallable,BlueprintPure=false) FString GetJsonAsString() const; /** * 文字列からJsonを構築する */ bool BuildJsonFromString(const FString& JsonString); private: UFUNCTION() void OnChangePropertyBool(const FName& PropertyName, UObject* PropertyOwnerObject, bool New, bool Old, const FString& PropertySaveKey); UFUNCTION() void OnChangePropertyInt(const FName& PropertyName, UObject* PropertyOwnerObject, int32 New, int32 Old, const FString& PropertySaveKey); UFUNCTION() void OnChangePropertyFloat(const FName& PropertyName, UObject* PropertyOwnerObject, float New, float Old, const FString& PropertySaveKey); UFUNCTION() void OnChangePropertyByte(const FName& PropertyName, UObject* PropertyOwnerObject, uint8 New, uint8 Old, const FString& PropertySaveKey); UFUNCTION() void OnChangePropertyString(const FName& PropertyName, UObject* PropertyOwnerObject, FString New, FString Old, const FString& PropertySaveKey); UFUNCTION() void OnChangePropertyVector(const FName& PropertyName, UObject* PropertyOwnerObject, FVector New, FVector Old, const FString& PropertySaveKey); UFUNCTION() void OnChangePropertyVector2D(const FName& PropertyName, UObject* PropertyOwnerObject, FVector2D New, FVector2D Old, const FString& PropertySaveKey); UFUNCTION() void OnChangePropertyRotator(const FName& PropertyName, UObject* PropertyOwnerObject, FRotator New, FRotator Old, const FString& PropertySaveKey); }; ``` `SampleProject/Plugins/GameDebugMenu/Source/GameDebugMenu/Public/Component/GDMSaveSystemComponent.h`: ```h /** * Copyright (c) 2020 akihiko moroi * * This software is released under the MIT License. * (See accompanying file LICENSE.txt or copy at http://opensource.org/licenses/MIT) */ #pragma once #include "CoreMinimal.h" #include "GDMPropertyJsonSystemComponent.h" #include "Components/ActorComponent.h" #include "GameFramework/SaveGame.h" #include "GDMSaveSystemComponent.generated.h" class UGDMSaveGame; /** * DebugMenuのセーブ/ロード機能を扱うコンポーネント */ UCLASS(NotBlueprintable, NotBlueprintType) class GAMEDEBUGMENU_API UGDMSaveSystemComponent : public UActorComponent { GENERATED_BODY() UPROPERTY() int32 UserIndex; UPROPERTY() TObjectPtr SaveGame; public: UGDMSaveSystemComponent(const FObjectInitializer& ObjectInitializer = FObjectInitializer::Get()); public: /** * DebugMenuの状態を保存する */ UFUNCTION(BlueprintCallable) virtual void SaveDebugMenuFile(); /** * DebugMenuの状態を読み込む */ UFUNCTION(BlueprintCallable) virtual void LoadDebugMenuFile(); /** * DebugMenuのセーブデータを削除する */ UFUNCTION(BlueprintCallable) virtual void DeleteDebugMenuFile(); protected: UGDMPropertyJsonSystemComponent* GetPropertyJsonSystemComponent() const; virtual bool SaveFile(const FString& ContentString); virtual bool LoadFile(FString& OutLoadedContentString); virtual bool DeleteFile(); bool CanUseSaveGame(); }; UCLASS() class UGDMSaveGame : public USaveGame { GENERATED_BODY() public: UPROPERTY() FString Json; }; ``` `SampleProject/Plugins/GameDebugMenu/Source/GameDebugMenu/Public/Component/GDMScreenshotRequesterComponent.h`: ```h /** * Copyright (c) 2020 akihiko moroi * * This software is released under the MIT License. * (See accompanying file LICENSE.txt or copy at http://opensource.org/licenses/MIT) */ #pragma once #include "CoreMinimal.h" #include "GDMScreenshotRequesterComponent.generated.h" class AGameDebugMenuManager; DECLARE_DYNAMIC_MULTICAST_DELEGATE(FGDMGameDebugMenuScreenshotDelegate); /** * DebugMenuでのスクショ処理 */ UCLASS(NotBlueprintable, NotBlueprintType) class GAMEDEBUGMENU_API UGDMScreenshotRequesterComponent : public UActorComponent { GENERATED_BODY() protected: FDelegateHandle OnScreenshotCapturedHandle; FDelegateHandle OnScreenshotRequestProcessedHandle; bool bRequestProcessed; public: UGDMScreenshotRequesterComponent(const FObjectInitializer& ObjectInitializer = FObjectInitializer::Get()); virtual void BeginPlay() override; virtual void EndPlay(const EEndPlayReason::Type EndPlayReason) override; public: virtual void RequestScreenshot(); virtual bool IsRequestProcessed(); protected: AGameDebugMenuManager* GetOwnerGameDebugMenuManager() const; virtual void OnScreenshotCaptured(int32 Width, int32 Height, const TArray& Bitmap); virtual void OnScreenshotRequestProcessed(); virtual void ResetHandle(); }; ``` `SampleProject/Plugins/GameDebugMenu/Source/GameDebugMenu/Public/ConsoleCommand/GDMConsoleCommandValueProvider.h`: ```h /** * Copyright (c) 2020 akihiko moroi * * This software is released under the MIT License. * (See accompanying file LICENSE.txt or copy at http://opensource.org/licenses/MIT) */ #pragma once #include "CoreMinimal.h" #include "UObject/Object.h" #include "GDMConsoleCommandValueProvider.generated.h" /** * コンソールコマンド名に対応する値を取得するためのベースクラス。 * 複数のコンソールコマンドへのアクセス方式(例: ConsoleVariable, WorldTime等)を抽象化する。 */ UCLASS(Abstract, Blueprintable, EditInlineNew, DefaultToInstanced) class GAMEDEBUGMENU_API UGDMConsoleCommandValueProvider : public UObject { GENERATED_BODY() public: UFUNCTION(BlueprintNativeEvent, BlueprintCallable) bool GetFloatValue(const FString& CommandName, float& OutValue) const; virtual bool GetFloatValue_Implementation(const FString& CommandName, float& OutValue) const { return false; } }; ``` `SampleProject/Plugins/GameDebugMenu/Source/GameDebugMenu/Public/ConsoleCommand/GDMConsoleCommandValueProviderComponent.h`: ```h /** * Copyright (c) 2020 akihiko moroi * * This software is released under the MIT License. * (See accompanying file LICENSE.txt or copy at http://opensource.org/licenses/MIT) */ #pragma once #include "CoreMinimal.h" #include "Components/ActorComponent.h" #include "GDMConsoleCommandValueProvider.h" #include "GDMConsoleCommandValueProviderComponent.generated.h" class UGDMConsoleVariableCommandValueProvider; /** * 特定の部分文字列(パターン)に一致するコンソールコマンドに対し、 * 対応する ValueProvider を指定する構造体。 */ USTRUCT(BlueprintType) struct FGDMConsoleCommandProviderPattern { GENERATED_BODY() UPROPERTY(EditAnywhere, BlueprintReadWrite) FString Pattern; UPROPERTY(EditAnywhere, Instanced, BlueprintReadWrite) TObjectPtr Provider = nullptr; }; /** * 登録されたパターンに応じて適切な UGDMConsoleCommandValueProvider を選択し、 * コンソールコマンドから値を取得する ActorComponent。 * * パターンマッチが成功しない場合は ConsoleVariableCommandValueProvider を fallback として利用する。 */ UCLASS() class GAMEDEBUGMENU_API UGDMConsoleCommandValueProviderComponent : public UActorComponent { GENERATED_BODY() protected: UPROPERTY(EditAnywhere, Category="GDM") TArray ProviderPatterns; UPROPERTY(EditAnywhere, Instanced, Category="GDM") TObjectPtr ConsoleVariableCommandValueProvider; public: UGDMConsoleCommandValueProviderComponent(); UFUNCTION(BlueprintCallable, Category = "Console") bool GetFloatValue(const FString& CommandName, float& OutValue); }; ``` `SampleProject/Plugins/GameDebugMenu/Source/GameDebugMenu/Public/ConsoleCommand/GDMConsoleVariableCommandValueProvider.h`: ```h /** * Copyright (c) 2020 akihiko moroi * * This software is released under the MIT License. * (See accompanying file LICENSE.txt or copy at http://opensource.org/licenses/MIT) */ #pragma once #include "CoreMinimal.h" #include "GDMConsoleCommandValueProvider.h" #include "GDMConsoleVariableCommandValueProvider.generated.h" /** * ConsoleManager用プロバイダー */ UCLASS() class GAMEDEBUGMENU_API UGDMConsoleVariableCommandValueProvider : public UGDMConsoleCommandValueProvider { GENERATED_BODY() public: virtual bool GetFloatValue_Implementation(const FString& CommandName, float& OutValue) const override; }; ``` `SampleProject/Plugins/GameDebugMenu/Source/GameDebugMenu/Public/ConsoleCommand/GDMSlomoCommandValueProvider.h`: ```h /** * Copyright (c) 2020 akihiko moroi * * This software is released under the MIT License. * (See accompanying file LICENSE.txt or copy at http://opensource.org/licenses/MIT) */ #pragma once #include "CoreMinimal.h" #include "GDMConsoleCommandValueProvider.h" #include "GDMSlomoCommandValueProvider.generated.h" /** * Slomo用プロバイダー */ UCLASS() class GAMEDEBUGMENU_API UGDMSlomoCommandValueProvider : public UGDMConsoleCommandValueProvider { GENERATED_BODY() public: virtual bool GetFloatValue_Implementation(const FString& CommandName, float& OutValue) const override; }; ``` `SampleProject/Plugins/GameDebugMenu/Source/GameDebugMenu/Public/Data/GDMConsoleCommandSetAsset.h`: ```h /** * Copyright (c) 2020 akihiko moroi * * This software is released under the MIT License. * (See accompanying file LICENSE.txt or copy at http://opensource.org/licenses/MIT) */ #pragma once #include "CoreMinimal.h" #include "GameDebugMenuTypes.h" #include "Engine/DataAsset.h" #include "GDMConsoleCommandSetAsset.generated.h" /** * デバッグメニューで使用可能なコンソールコマンド群を定義するためのデータアセット * AGameDebugMenuManager によって参照され、ユーザーがゲーム中にデバッグUIを通じてコマンドを実行できるようにするものです * コマンド情報はINIファイルで設定されたものを一括追加、他アセットのマージなど可能 */ UCLASS(Const, BlueprintType) class GAMEDEBUGMENU_API UGDMConsoleCommandSetAsset : public UDataAsset { GENERATED_BODY() public: /** コンソールコマンド名 */ UPROPERTY(EditAnywhere, Category = "ConsoleCommand") TArray ConsoleCommandNames; /** コンソールコマンドグループ名(同時に複数コマンド実行) */ UPROPERTY(EditAnywhere, Category = "ConsoleCommand") TArray ConsoleCommandGroups; /** コンソールコマンドペア名(トグルで2種のコマンド実行) */ UPROPERTY(EditAnywhere, Category = "ConsoleCommand") TArray ConsoleCommandPairs; /** コンソールコマンドナンバー(数値設定コマンド実行) */ UPROPERTY(EditAnywhere, Category = "ConsoleCommand") TArray ConsoleCommandNumbers; /** エディターでのみ追加されるコンソールコマンド名 */ UPROPERTY(EditAnywhere, Category = "ConsoleCommand") TArray EditorOnlyConsoleCommandNames; /** エディターでのみ追加されるコンソールコマンドグループ名 */ UPROPERTY(EditAnywhere, Category = "ConsoleCommand") TArray EditorOnlyConsoleCommandGroups; /** エディターでのみ追加されるコンソールコマンドペア名 */ UPROPERTY(EditAnywhere, Category = "ConsoleCommand") TArray EditorOnlyConsoleCommandPairs; /** エディターでのみ追加されるコンソールコマンドペア名 */ UPROPERTY(EditAnywhere, Category = "ConsoleCommand") TArray EditorOnlyConsoleCommandNumbers; /** コンソールコマンドのカテゴリ名表示順 */ UPROPERTY(EditAnywhere, EditFixedSize, Category = "ConsoleCommand") TArray OrderConsoleCommandCategoryTitles; public: #if WITH_EDITORONLY_DATA UPROPERTY(EditAnywhere, Category = "Editor") TArray CategoryIndexList; /** マージ対象の外部アセット */ UPROPERTY(EditAnywhere, Category = "Editor") TArray MergeSourceAssets; #endif /** * INIファイルで設定されたコマンド郡をこのアセットにセットする */ UFUNCTION(CallInEditor, Category = "Editor") void SetupCommandNames(); /** * INIファイルで設定されたコマンドのカテゴリ順をこのアセットにセットする */ UFUNCTION(CallInEditor, Category = "Editor") void SetupOrderConsoleCommandCategoryTitles(); /** * アセットをマージする */ UFUNCTION(CallInEditor, Category = "Editor") void MergeFromSourceAssets(); public: /** * コンソールコマンドメニューのカテゴリ名を取得 */ UFUNCTION(BlueprintCallable, BlueprintPure=false) TArray GetOrderConsoleCommandCategoryTitle() const; /** * コンソールコマンドの情報取得 */ UFUNCTION(BlueprintCallable, BlueprintPure=false) bool GetConsoleCommandNameByArrayIndex(const int32 ArrayIndex, FGDMConsoleCommandSingle& Out) const; /** * コンソールコマンド数 */ UFUNCTION(BlueprintPure) int32 GetNumConsoleCommandNames() const; /** * コンソールコマンドグループの情報取得 */ UFUNCTION(BlueprintCallable, BlueprintPure=false) bool GetConsoleCommandGroupByArrayIndex(const int32 ArrayIndex, FGDMConsoleCommandGroup& Out) const; /** * コンソールコマンドグループ数 */ UFUNCTION(BlueprintPure) int32 GetNumConsoleCommandGroups() const; /** * コンソールコマンドペアの情報取得 */ UFUNCTION(BlueprintCallable, BlueprintPure=false) bool GetConsoleCommandPairByArrayIndex(const int32 ArrayIndex, FGDMConsoleCommandPair& Out) const; /** * コンソールコマンドペア数 */ UFUNCTION(BlueprintPure) int32 GetNumConsoleCommandPairs() const; /** * コンソールコマンドナンバーの情報取得 */ UFUNCTION(BlueprintCallable, BlueprintPure=false) bool GetConsoleCommandNumberByArrayIndex(const int32 ArrayIndex, FGDMConsoleCommandNumber& Out) const; /** * コンソールコマンドナンバー数 */ UFUNCTION(BlueprintPure) int32 GetNumConsoleCommandNumbers() const; UFUNCTION(BlueprintCallable, BlueprintPure=false) bool FindConsoleCommandSingleById(const FString& CommandId, FGDMConsoleCommandSingle& Out) const; UFUNCTION(BlueprintCallable, BlueprintPure=false) bool FindConsoleCommandGroupById(const FString& CommandId, FGDMConsoleCommandGroup& Out) const; UFUNCTION(BlueprintCallable, BlueprintPure=false) bool FindConsoleCommandPairById(const FString& CommandId, FGDMConsoleCommandPair& Out) const; UFUNCTION(BlueprintCallable, BlueprintPure=false) bool FindConsoleCommandNumberById(const FString& CommandId, FGDMConsoleCommandNumber& Out) const; }; ``` `SampleProject/Plugins/GameDebugMenu/Source/GameDebugMenu/Public/Data/GameDebugMenuManagerAsset.h`: ```h /** * Copyright (c) 2020 akihiko moroi * * This software is released under the MIT License. * (See accompanying file LICENSE.txt or copy at http://opensource.org/licenses/MIT) */ #pragma once #include "CoreMinimal.h" #include "GameDebugMenuTypes.h" #include "Engine/DataAsset.h" #include "GameDebugMenuManagerAsset.generated.h" class AGDMDebugCameraInput; class UGDMPlayerControllerProxyComponent; class UGameDebugMenuRootWidget; class UGameDebugMenuWidget; class UGDMFavoriteItemDefinition; /** * デバッグメニューの構成をまとめたデータアセット * このアセットは、GameDebugMenuManager によって参照され、 * 表示するデバッグメニューUIの構成・入力設定などメニュー全体の挙動を制御する設定を一元的に保持 */ UCLASS(Const) class GAMEDEBUGMENU_API UGameDebugMenuManagerAsset : public UDataAsset { GENERATED_BODY() public: /** メインとなるデバックメニューのWidgetクラス */ UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "Widget") TSubclassOf DebugMenuRootWidgetClass; /** 各メニューのWidget郡(Key=メニュー識別名, Value=メニュークラス) */ UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "Widget") TMap > DebugMenuClasses; /** 各メニューのUI登録順(メニュー識別名を指定する) */ UPROPERTY(EditAnywhere, BlueprintReadOnly,Category = "Widget") TArray DebugMenuRegistrationOrder; /** メインとなるデバックメニューのWidgetのZオーダー値 */ UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "Widget") int32 RootWidgetZOrder; /** DebugMenuManagerが作られたときに追加される InputMappingContext */ UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "Input") TArray AddInputMappingContextWhenCreateManager; /** DebugMenuを表示するときに追加される InputMappingContext */ UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "Input") TArray AddInputMappingContextWhenDebugMenuIsShow; /** デバックカメラ用のインプットアクター */ UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "Input") TSubclassOf DebugCameraInputClass; /** お気に入りデータの定義 */ UPROPERTY(EditAnywhere, Instanced, category = "Favorite") TArray> FavoriteItemDefinitions; /** PlayerControllerに自動追加されるコンポーネント */ UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "Other") TSubclassOf DebugMenuPCProxyComponentClass; /** True : デバックメニュー操作中ポーズする */ UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "Other") bool bGamePause; public: UGameDebugMenuManagerAsset(); }; ``` `SampleProject/Plugins/GameDebugMenu/Source/GameDebugMenu/Public/Data/GameDebugMenuMasterAsset.h`: ```h /** * Copyright (c) 2020 akihiko moroi * * This software is released under the MIT License. * (See accompanying file LICENSE.txt or copy at http://opensource.org/licenses/MIT) */ #pragma once #include "CoreMinimal.h" #include "Engine/DataAsset.h" #include "GameDebugMenuTypes.h" #include "GameDebugMenuMasterAsset.generated.h" class AGDMDebugReportRequester; class UGDMEnhancedInputComponent; class AGameDebugMenuManager; /** * デバッグメニュー全体で使用されるアセット情報をまとめたアセット * デバッグメニューに関係する構成要素への参照を一元化して保持 * UPrimaryDataAsset なのでPrimaryAssetTypesToScanに追加が必要。(GetPrimaryType参照) * UGameDebugMenuSettingsのMasterAssetNameにこのアセットを指定することで使用できる */ UCLASS(Const) class GAMEDEBUGMENU_API UGameDebugMenuMasterAsset : public UPrimaryDataAsset { GENERATED_BODY() public: /** 使用するマネージャークラス */ UPROPERTY(EditAnywhere, Category = "Manager") TArray> DebugMenuManagerClasses; /** メニューの入力処理をするコンポーネント */ UPROPERTY(EditAnywhere, Category = "Input") TSoftClassPtr DebugMenuInputComponentClass; /** バグレポート処理クラス */ UPROPERTY(EditAnywhere, Category = "ReportSettings") TMap> DebugReportRequesterClass; /** デバックメニュー用StringTable */ UPROPERTY(EditAnywhere, Category = "Localization") TMap GameDebugMenuStringTables; /** デバックメニューのUMG使用フォント */ UPROPERTY(EditAnywhere, Category = "Font", meta = (AllowedClasses = "/Script/Engine.Font", DisplayName = "Font Family")) FSoftObjectPath FontName; public: UGameDebugMenuMasterAsset(const FObjectInitializer& ObjectInitializer); virtual FPrimaryAssetId GetPrimaryAssetId() const override; static const FPrimaryAssetType& GetPrimaryType(); TSoftClassPtr GetGameDebugMenuManagerSoftClass(FString ClassName) const; }; ``` `SampleProject/Plugins/GameDebugMenu/Source/GameDebugMenu/Public/Favorite/GDMFavoriteItemDefinition.h`: ```h /** * Copyright (c) 2020 akihiko moroi * * This software is released under the MIT License. * (See accompanying file LICENSE.txt or copy at http://opensource.org/licenses/MIT) */ #pragma once #include "CoreMinimal.h" #include "UObject/Object.h" #include "GDMFavoriteItemDefinition.generated.h" class AGameDebugMenuManager; class UUserWidget; /** * お気に入り登録できるWidget情報を定義 */ UCLASS(Abstract, Blueprintable, EditInlineNew, CollapseCategories) class GAMEDEBUGMENU_API UGDMFavoriteItemDefinition : public UObject { GENERATED_BODY() public: /* お気に入り登録されるWidgetクラス */ UPROPERTY(EditAnywhere, BlueprintReadOnly) TSubclassOf FavoriteWidgetClass = nullptr; TWeakObjectPtr OwnerManager = nullptr; public: virtual UWorld* GetWorld() const override; UFUNCTION(BlueprintPure, Category = "GDM") AGameDebugMenuManager* GetOwnerManager() const; /** * この定義がサポートするWidgetか? */ UFUNCTION(BlueprintNativeEvent, Category = "GDM") bool IsSupportedWidget(UUserWidget* Widget) const; virtual bool IsSupportedWidget_Implementation(UUserWidget* Widget) const PURE_VIRTUAL(, return false;); /** * エクスポート処理 * @return Widgetを復元するために必要な識別キーを返す */ UFUNCTION(BlueprintNativeEvent, Category = "GDM") FString ExportToFavoriteKey(UUserWidget* TargetWidget); virtual FString ExportToFavoriteKey_Implementation(UUserWidget* TargetWidget) PURE_VIRTUAL(, return FString();); /** * インポート処理+Widget生成 * @param FavoriteSaveKey - Widgetを復元するために必要な識別キー */ UFUNCTION(BlueprintNativeEvent, Category = "GDM") UUserWidget* CreateWidgetFromFavoriteData(const FString& FavoriteSaveKey); virtual UUserWidget* CreateWidgetFromFavoriteData_Implementation(const FString& FavoriteSaveKey) PURE_VIRTUAL(, return nullptr;); }; ``` `SampleProject/Plugins/GameDebugMenu/Source/GameDebugMenu/Public/Favorite/GDMFavoriteSystemComponent.h`: ```h /** * Copyright (c) 2020 akihiko moroi * * This software is released under the MIT License. * (See accompanying file LICENSE.txt or copy at http://opensource.org/licenses/MIT) */ #pragma once #include "CoreMinimal.h" #include "GameDebugMenuTypes.h" #include "Components/ActorComponent.h" #include "GDMFavoriteSystemComponent.generated.h" class AGameDebugMenuManager; class UGDMFavoriteItemDefinition; class UGameDebugMenuManagerAsset; class UUserWidget; class UGDMPropertyJsonSystemComponent; /** * メニューのお気に入りシステム制御コンポーネント */ UCLASS() class GAMEDEBUGMENU_API UGDMFavoriteSystemComponent : public UActorComponent { GENERATED_BODY() /** お気に入り情報 */ UPROPERTY() TArray> CachedFavoriteDefinitions; public: UGDMFavoriteSystemComponent(); protected: UGDMPropertyJsonSystemComponent* GetPropertyJsonSystemComponent() const; public: virtual void Initialize(const UGameDebugMenuManagerAsset* InMenuAsset); /** * お気に入りに追加/削除を行う * @param TargetWidget - お気に入り対象のWidget * @return true 追加/削除を行った false お気に入り処理の対象外のWidgetだった */ UFUNCTION(BlueprintCallable) virtual bool ToggleAddOrRemoveFavorite(UUserWidget* TargetWidget); /** * お気に入りに追加する */ UFUNCTION(BlueprintCallable) virtual void AddFavorite(UUserWidget* TargetWidget); /** * お気に入りから削除 */ UFUNCTION(BlueprintCallable) virtual void RemoveFavorite(UUserWidget* TargetWidget); /** * お気に入り処理ができるWidgetか? */ UFUNCTION(BlueprintPure) virtual bool CanFavorite(UUserWidget* TargetWidget); /** * お気に入り中か? */ UFUNCTION(BlueprintPure) virtual bool IsFavorited(UUserWidget* TargetWidget); /** * お気に入りから全削除をする */ UFUNCTION(BlueprintCallable) virtual void ClearAllFavorites(); /** * 指定のお気に入り情報が現在の情報と一致してるか? * @return true 完全一致 */ UFUNCTION(BlueprintPure) bool IsFavoriteListEqualTo(const TArray& OtherList) const; /** * お気に入り情報から関連するWidgetを生成し返す */ UFUNCTION(BlueprintCallable) virtual UUserWidget* CreateFavoriteWidgetFromEntry(const FGDMFavoriteEntry& Entry); }; ``` `SampleProject/Plugins/GameDebugMenu/Source/GameDebugMenu/Public/GameDebugMenu.h`: ```h /** * Copyright (c) 2020 akihiko moroi * * This software is released under the MIT License. * (See accompanying file LICENSE.txt or copy at http://opensource.org/licenses/MIT) */ #pragma once #include "Modules/ModuleManager.h" class GAMEDEBUGMENU_API FGameDebugMenuModule : public IModuleInterface { public: /* Begin IModuleInterface */ virtual void StartupModule() override; virtual void ShutdownModule() override; /* End IModuleInterface */ }; ``` `SampleProject/Plugins/GameDebugMenu/Source/GameDebugMenu/Public/GameDebugMenuFunctions.h`: ```h /** * Copyright (c) 2020 akihiko moroi * * This software is released under the MIT License. * (See accompanying file LICENSE.txt or copy at http://opensource.org/licenses/MIT) */ #pragma once #include "Kismet/BlueprintFunctionLibrary.h" #include "GameDebugMenuManager.h" #include "GameDebugMenuTypes.h" #include "GameDebugMenuFunctions.generated.h" class UWidget; UCLASS() class GAMEDEBUGMENU_API UGameDebugMenuFunctions : public UBlueprintFunctionLibrary { GENERATED_BODY() static TArray RegisterPendingProperties; static TArray RegisterPendingFunctions; static FDelegateHandle ActorSpawnedDelegateHandle; static bool bDisableGameDebugMenu; public: UGameDebugMenuFunctions(const FObjectInitializer& ObjectInitializer); public: static void RegisterGameDebugMenuManagerInstance(AGameDebugMenuManager* RegisterManager); static void UnregisterGameDebugMenuManagerInstance(AGameDebugMenuManager* UnregisterManager); public: /** * 生成。ロードしてない場合は同期ロードされるので注意 * @param PlayerController - メニューを操作するローカルのプレイヤーコントローラー * @param DebugMenuManagerClassName - 生成するマネージャークラス名(BP_GDM_Manager) * @return true: 正常終了 false: デバックメニューは使用できない */ UFUNCTION(BlueprintCallable, Category = "GDM|Functions", meta = (Keywords = "DebugMenu GDM")) static bool TryCreateDebugMenuManager(APlayerController* PlayerController, FString DebugMenuManagerClassName); /** * 削除 */ UFUNCTION(BlueprintCallable, Category = "GDM|Functions", meta = (Keywords = "DebugMenu GDM")) static bool DestroyDebugMenuManager(APlayerController* PlayerController); /** * 取得(Managerの参照をもつことになるので使用する場合は注意) */ UFUNCTION(BlueprintPure, Category = "GDM|Functions", meta = (Keywords = "DebugMenu GDM", WorldContext = "WorldContextObject", AdvancedDisplay = "bCheckInitialize")) static AGameDebugMenuManager* GetGameDebugMenuManager(const UObject* WorldContextObject, bool bCheckInitialize = true); /** * 取得(Managerの参照をもつことになるので使用する場合は注意) */ UFUNCTION(BlueprintPure, Category = "GDM|Functions", meta = (Keywords = "DebugMenu GDM")) static AGameDebugMenuManager* TryGetGameDebugMenuManagerFromPlayerController(const APlayerController* PlayerController); /** * 表示 * @return True: 処理の正常終了 */ UFUNCTION(BlueprintCallable, BlueprintCosmetic, Category = "GDM|Functions", meta = (Keywords = "DebugMenu GDM", WorldContext = "WorldContextObject")) static bool ShowDebugMenu(UObject* WorldContextObject); /** * 非表示 * @return True: 処理の正常終了 */ UFUNCTION(BlueprintCallable, BlueprintCosmetic, Category = "GDM|Functions", meta = (Keywords = "DebugMenu GDM", WorldContext = "WorldContextObject")) static bool HideDebugMenu(UObject* WorldContextObject); /** * (非)表示に切り替える * @return True: 処理の正常終了 */ UFUNCTION(BlueprintCallable, BlueprintCosmetic, Category = "GDM|Functions", meta = (Keywords = "DebugMenu GDM", WorldContext = "WorldContextObject")) static bool ToggleDebugMenu(UObject* WorldContextObject); /** * True: 表示中 False:非表示 */ UFUNCTION(BlueprintCallable, BlueprintCosmetic, Category = "GDM|Functions", meta = (Keywords = "DebugMenu GDM", WorldContext = "WorldContextObject")) static bool IsShowingDebugMenu(UObject* WorldContextObject); /** * RootになるWidget取得(Widgetの参照をもつことになるので使用する場合は注意) */ UFUNCTION(BlueprintPure, BlueprintCosmetic, Category = "GDM|Functions", meta = (Keywords = "DebugMenu GDM", WorldContext = "WorldContextObject")) static UGameDebugMenuRootWidget* GetGameDebugMenuRootWidget(const UObject* WorldContextObject); /** * DebugMenuに表示させるオブジェクトのプロパティを登録する * @param TargetObject - プロパティを所持してる対象のオブジェクト * @param PropertyUIConfigInfo - UIでの設定情報 * @param PropertyName - プロパティ名 * @param CategoryKey - カテゴリ * @param PropertySaveKey - プロパティデータ保存用の識別キー * @param DisplayPropertyName - UI上のプロパティ名,何も指定しなければ「PropertyName」で表示 * @param Description - 説明文 * @param DisplayPriority - リスト追加時の表示優先度(降順) * @return True: 登録成功 False: 何らかの要因で失敗した(ログに出力されてます) */ UFUNCTION(BlueprintCallable, BlueprintCosmetic, Category = "GDM|Functions", meta = (Keywords = "DebugMenu GDM", AdvancedDisplay = "5")) static bool RegisterGDMObjectProperty(UObject* TargetObject, const FGDMPropertyUIConfigInfo PropertyUIConfigInfo, const FName PropertyName, const FGDMGameplayCategoryKey CategoryKey, const FString PropertySaveKey, const FText DisplayPropertyName,const FText Description,const int32 DisplayPriority); /** * DebugMenuに表示させるオブジェクトの関数(カスタムイベント)を登録する * @param TargetObject - 関数(カスタムイベント)を所持してる対象のオブジェクト * @param FunctionName - 関数(カスタムイベント)名 * @param CategoryKey - カテゴリ * @param FunctionSaveKey - 関数保存用の識別キー * @param DisplayFunctionName - UI上の関数(カスタムイベント)名,何も指定しなければ「FunctionName」 * @param Description - 説明文 * @param DisplayPriority - リスト追加時の表示優先度(降順) * @return True: 登録成功 False: 何らかの要因で失敗した(ログに出力されてます) * @note ここで登録できるのは引数0の関数(カスタムイベント)のみ */ UFUNCTION(BlueprintCallable, BlueprintCosmetic, Category = "GDM|Functions", meta = (Keywords = "DebugMenu GDM", AdvancedDisplay = "3")) static bool RegisterGDMObjectFunction(UObject* TargetObject, FName FunctionName, const FGDMGameplayCategoryKey CategoryKey, const FString FunctionSaveKey, const FText DisplayFunctionName,const FText Description, const int32 DisplayPriority); /** * 対象のオブジェクトが登録したプロパティ&関数を解除する */ UFUNCTION(BlueprintCallable, BlueprintCosmetic, Category = "GDM|Functions", meta = (Keywords = "DebugMenu GDM")) static void UnregisterGDMObject(UObject* TargetObject); /** * 登録済みプロパティ情報を取得する */ UFUNCTION(BlueprintCallable, BlueprintCosmetic, Category = "GDM|Functions", meta = (Keywords = "DebugMenu GDM", WorldContext = "WorldContextObject")) static UObject* GetGDMObjectProperty(UObject* WorldContextObject,const int32 Index, FGDMGameplayCategoryKey& OutCategoryKey, FString& OutPropertySaveKey, FText& OutDisplayPropertyName, FText& OutDescription, FName& OutPropertyName, EGDMPropertyType& OutPropertyType, FString& OutEnumPathName, FGDMPropertyUIConfigInfo& PropertyUIConfigInfo); /** * 登録済み関数(カスタムイベント)を取得する */ UFUNCTION(BlueprintCallable, BlueprintCosmetic, Category = "GDM|Functions", meta = (Keywords = "DebugMenu GDM", WorldContext = "WorldContextObject")) static UObject* GetGDMObjectFunction(UObject* WorldContextObject,const int32 Index, FGDMGameplayCategoryKey& OutCategoryKey, FString& OutFunctionSaveKey, FText& OutDisplayFunctionName, FText& OutDescription, FName& OutFunctionName); /** * 登録済みプロパティ数を取得 */ UFUNCTION(BlueprintPure, BlueprintCosmetic, Category = "GDM|Functions", meta = (Keywords = "DebugMenu GDM", WorldContext = "WorldContextObject")) static int32 GetGDMNumObjectProperties(UObject* WorldContextObject); /** * 登録済み関数(カスタムイベント)数を取得 */ UFUNCTION(BlueprintPure, BlueprintCosmetic, Category = "GDM|Functions", meta = (Keywords = "DebugMenu GDM", WorldContext = "WorldContextObject")) static int32 GetGDMNumObjectFunctions(UObject* WorldContextObject); /** * 登録済みプロパティが使用できるか確認する * @return True すべて問題なし False 1つ以上使用できないものがあった */ UFUNCTION(BlueprintCallable, BlueprintCosmetic, Category = "GDM|Functions", meta = (Keywords = "DebugMenu GDM", WorldContext = "WorldContextObject")) static bool VerifyGDMNumObjectProperties(UObject* WorldContextObject); /** * 登録済み関数が使用できるか確認する * @return True すべて問題なし False 1つ以上使用できないものがあった */ UFUNCTION(BlueprintCallable, BlueprintCosmetic, Category = "GDM|Functions", meta = (Keywords = "DebugMenu GDM", WorldContext = "WorldContextObject")) static bool VerifyGDMNumObjectFunctions(UObject* WorldContextObject); /** * Gameplayメニューのカテゴリ名を取得 */ UFUNCTION(BlueprintCallable, Category = "GDM|Functions", meta = (Keywords = "DebugMenu GDM", WorldContext = "WorldContextObject")) static TArray GetGDMOrderGameplayCategoryTitle(UObject* WorldContextObject); /** * 無効なIndex */ UFUNCTION(BlueprintPure, Category = "GDM|Functions", meta = (Keywords = "DebugMenu GDM")) static int32 GetGDMInvalidIndex(); /** * オブジェクト名を取得する * 内部ではEditorならGetDisplayNameそれ以外でGetObjectNameを呼ぶ */ UFUNCTION(BlueprintPure,Category = "GDM|Functions",meta = ( Keywords = "DebugMenu GDM" )) static FString GetGDMObjectName(UObject* TargetObject); /** * デバッグメニュー操作用のInputComponentを登録する */ UFUNCTION(BlueprintCallable, BlueprintCosmetic, Category = "GDM|Functions", meta = (Keywords = "DebugMenu GDM", WorldContext = "WorldContextObject")) static void RegisterInputComponentForGameDebugMenu(UObject* WorldContextObject, UInputComponent* InputComponent); /** * デバッグメニュー操作用のInputComponentを解除する */ UFUNCTION(BlueprintCallable, BlueprintCosmetic, Category = "GDM|Functions", meta = (Keywords = "DebugMenu GDM", WorldContext = "WorldContextObject")) static void UnregisterInputComponentForGameDebugMenu(UObject* WorldContextObject, UInputComponent* InputComponent); /** * DebugReport用UIを表示する * @note 通常の開閉は最後に操作したMenuになるがこっちはなからずDebugReport用UIを表示されキャプチャもDebugMenuが含まれない */ UFUNCTION(BlueprintCallable, BlueprintCosmetic, Category = "GDM|Functions", meta = (Keywords = "DebugMenu GDM", WorldContext = "WorldContextObject")) static bool ShowDebugReport(UObject* WorldContextObject); /** * DebugReport用UIが対応するツールを取得 */ UFUNCTION(BlueprintPure, BlueprintCosmetic, Category = "GDM|Functions", meta = (Keywords = "DebugMenu GDM")) static EGDMProjectManagementTool GetGDMSelectedProjectManagementTool(); /** * ビルド構成を文字列で取得 * @note EBuildConfiguration(Engine\Source\Runtime\Core\Public\GenericPlatform\GenericPlatformMisc.h)参照 */ UFUNCTION(BlueprintPure, BlueprintCosmetic, Category = "GDM|Functions", meta = (Keywords = "DebugMenu GDM")) static FString GetGDMBuildConfigurationString(); /** * エンジンのビルドバージョンを取得 */ UFUNCTION(BlueprintPure, BlueprintCosmetic, Category = "GDM|Functions", meta = (Keywords = "DebugMenu GDM")) static FString GetGDMBuildVersionString(); /** * プロジェクトバージョンの取得 */ UFUNCTION(BlueprintPure, BlueprintCosmetic, Category = "GDM|Functions", meta = (Keywords = "DebugMenu GDM")) static FString GetGDMProjectVersionString(); /** * デバッグメニュー用のログカテゴリを使用したPrintString */ UFUNCTION(BlueprintCallable, BlueprintCosmetic, Category = "GDM|Functions", meta = (Keywords = "DebugMenu GDM", WorldContext = "WorldContextObject", DisplayName = "PrintLogScreenGDM")) static void PrintLogScreen(UObject* WorldContextObject, const FString& InString, float Duration = 0.2f, bool bPrintToLog = false); /** * デバッグメニュー用のStringTableから文字列を取得する * @param StringKey - DebugMenuのStringTableのキー * @param OutString - キー対応する文字列 * @return true:成功。キーに合う文字列を取得 false:失敗。キーが存在しないかマネージャーが生成されてない */ UFUNCTION(BlueprintCallable, Category = "GDM|Functions", meta = (Keywords = "DebugMenu GDM", WorldContext = "WorldContextObject")) static bool GetDebugMenuString(UObject* WorldContextObject, const FString StringKey, FString& OutString); /** * デバッグメニューから切り替えできる言語を取得(エンジン内の言語切り替え) */ UFUNCTION(BlueprintPure, BlueprintCosmetic, Category = "GDM|Functions", meta = (Keywords = "DebugMenu GDM")) static TArray GetGDMCultureList(); /** * デバッグメニューの現在使用中の言語を取得 */ UFUNCTION(BlueprintPure, Category = "GDM|Functions", meta = (Keywords = "DebugMenu GDM", WorldContext = "WorldContextObject")) static FName GetCurrentDebugMenuLanguage(UObject* WorldContextObject); /** * デバッグメニューで使用できる言語を取得 */ UFUNCTION(BlueprintCallable, Category = "GDM|Functions", meta = (Keywords = "DebugMenu GDM")) static TArray GetDebugMenuLanguageKeys(); /** * デバッグメニューで使用する共通の改行判定文字を取得 */ UFUNCTION(BlueprintPure, Category = "GDM|Functions", meta = (Keywords = "DebugMenu GDM")) static FString GetDebugMenuLineBreakString(); /** 単体コマンドから識別子を構築 */ UFUNCTION(BlueprintCallable, Category = "ConsoleCommand") static FString BuildConsoleCommandId_FromSingle(const FGDMConsoleCommandSingle& Command); /** グループコマンドから識別子を構築 */ UFUNCTION(BlueprintCallable, Category = "ConsoleCommand") static FString BuildConsoleCommandId_FromGroup(const FGDMConsoleCommandGroup& Command); /** ペアコマンドから識別子を構築 */ UFUNCTION(BlueprintCallable, Category = "ConsoleCommand") static FString BuildConsoleCommandId_FromPair(const FGDMConsoleCommandPair& Command); /** 数値指定コマンドから識別子を構築 */ UFUNCTION(BlueprintCallable, Category = "ConsoleCommand") static FString BuildConsoleCommandId_FromNumber(const FGDMConsoleCommandNumber& Command); public: UFUNCTION(BlueprintPure, meta = (DisplayName = "Equal (GDMMenuCategoryKey)", CompactNodeTitle = "=="), Category = "GDM|Functions") static bool EqualEqual_GDMMenuCategoryKey(const FGDMMenuCategoryKey& A, const FGDMMenuCategoryKey& B); UFUNCTION(BlueprintPure, meta = (DisplayName = "NotEqual (GDMMenuCategoryKey)", CompactNodeTitle = "!="), Category = "GDM|Functions") static bool NotEqual_GDMMenuCategoryKey(const FGDMMenuCategoryKey& A, const FGDMMenuCategoryKey& B); UFUNCTION(BlueprintPure, meta = (DisplayName = "ToByte (GDMMenuCategoryKey)", CompactNodeTitle = "->", BlueprintAutocast), Category = "GDM|Functions") static uint8 Conv_GDMMenuCategoryKeyToByte(const FGDMMenuCategoryKey& Key); UFUNCTION(BlueprintPure, meta = (DisplayName = "ToGDMMenuCategoryKey (Byte)", CompactNodeTitle = "->", BlueprintAutocast), Category = "GDM|Functions") static FGDMMenuCategoryKey Conv_ByteToGDMMenuCategoryKey(const uint8& Key); private: static void OnActorSpawnedClientWaitManager(AGameDebugMenuManager* SpawnDebugMenuManager); static void OnActorSpawnedServer(AActor* SpawnActor); static void ShowDebugConsoleCommand(); static void HideDebugConsoleCommand(); static void ToggleDebugConsoleCommand(); static void ToggleInputSystemLog(); }; ``` `SampleProject/Plugins/GameDebugMenu/Source/GameDebugMenu/Public/GameDebugMenuManager.h`: ```h /** * Copyright (c) 2020 akihiko moroi * * This software is released under the MIT License. * (See accompanying file LICENSE.txt or copy at http://opensource.org/licenses/MIT) */ #pragma once #include "CoreMinimal.h" #include "GameFramework/Actor.h" #include "GameDebugMenuTypes.h" #include "GameDebugMenuManager.generated.h" class UGDMConsoleCommandSetAsset; class UGDMFavoriteSystemComponent; class UGDMConsoleCommandValueProviderComponent; class UGDMLocalizeStringComponent; class UGDMSaveSystemComponent; class UGDMPropertyJsonSystemComponent; class UGDMListenerComponent; class UGameDebugMenuWidget; class UGDMInputSystemComponent; class UGDMScreenshotRequesterComponent; class UGameDebugMenuManagerAsset; class UGameDebugMenuRootWidget; class UGDMPlayerControllerProxyComponent; class AGDMDebugReportRequester; class FGDMOutputDevice; /** * デバックメニュー管理マネージャー(生成はTryCreateDebugMenuManagerで) */ UCLASS(Blueprintable, BlueprintType, notplaceable) class GAMEDEBUGMENU_API AGameDebugMenuManager : public AActor { GENERATED_BODY() protected: /** DebugMenuの入力操作を制御するコンポーネント */ UPROPERTY(VisibleAnywhere, Category = "GDM", meta = (AllowPrivateAccess = "true")) TObjectPtr DebugMenuInputSystemComponent; /** DebugMenuで使用するスクリーンショットを制御するコンポーネント */ UPROPERTY(VisibleAnywhere, Category = "GDM", meta = (AllowPrivateAccess = "true")) TObjectPtr ScreenshotRequesterComponent; UPROPERTY(VisibleAnywhere, Category = "GDM", meta = (AllowPrivateAccess = "true")) TObjectPtr PropertyJsonSystemComponent; UPROPERTY(VisibleAnywhere, Category = "GDM", meta = (AllowPrivateAccess = "true")) TObjectPtr SaveSystemComponent; UPROPERTY(VisibleAnywhere, Category = "GDM", meta = (AllowPrivateAccess = "true")) TObjectPtr FavoriteSystemComponent; UPROPERTY(VisibleAnywhere, Category = "GDM", meta = (AllowPrivateAccess = "true")) TObjectPtr ConsoleCommandValueProviderComponent; UPROPERTY(VisibleAnywhere, Category = "GDM", meta = (AllowPrivateAccess = "true")) TObjectPtr LocalizeStringComponent; /** DebugMenuの各イベント検知コンポーネント */ UPROPERTY(VisibleAnywhere, Category = "GDM", meta = (AllowPrivateAccess = "true")) TObjectPtr ListenerComponent; FTimerHandle InitializeManagerHandle; bool bInitializedManager = false; /** BeginPlay時点でOwnerが未確定なことがあるため、ローカル判定ができるまで短時間リトライする */ FTimerHandle LocalBeginPlayRetryHandle; bool bLocalBeginPlayInitialized = false; /** デバックメニュー用UIアセット */ UPROPERTY(EditAnywhere, Category = "GDM") TObjectPtr MenuAsset; /** コンソールコマンド設定アセット */ UPROPERTY(EditAnywhere, Category = "GDM") TObjectPtr ConsoleCommandSetAsset; /** True:UI表示中 */ bool bShowDebugMenu; /** メニュー開く前のポーズ判定 */ bool bCachedGamePaused; /** メニュー開く前のマウスカーソル判定 */ bool bCachedShowMouseCursor; /** スクリーンキャプチャ後レポート用UIを開く */ bool bWaitToCaptureBeforeOpeningDebugReportMenu; /** メニュー開く前のナビゲーション情報 */ TArray> CachedNavigationConfigs; /** 登録済みプロパティ群 */ TArray> ObjectProperties; /** 登録済み関数群 */ TArray> ObjectFunctions; /** Viewport上に追加されてるメインWidget */ UPROPERTY(Transient) TObjectPtr DebugMenuRootWidget; /** 生成した各メニューWidgetのインスタンス */ UPROPERTY(Transient,BlueprintReadOnly, Category = "GDM") TMap> DebugMenuInstances; /** Viewport上に追加されてるメニューWidgetのインスタンス */ UPROPERTY(Transient) TArray> ViewportDebugMenuWidgets; /** DebugMenuのログデバイス(レポート送信用) */ TSharedPtr OutputLog; public: AGameDebugMenuManager(const FObjectInitializer& ObjectInitializer); protected: virtual void BeginPlay() override; public: virtual void EndPlay(const EEndPlayReason::Type EndPlayReason) override; virtual void Tick(float DeltaTime) override; public: UFUNCTION(BlueprintPure) UGDMInputSystemComponent* GetDebugMenuInputSystemComponent() const; UFUNCTION(BlueprintPure) UGDMScreenshotRequesterComponent* GetScreenshotRequesterComponent() const; UFUNCTION(BlueprintPure) UGDMPropertyJsonSystemComponent* GetPropertyJsonSystemComponent() const; UFUNCTION(BlueprintPure) UGDMSaveSystemComponent* GetSaveSystemComponent() const; UFUNCTION(BlueprintPure) UGDMFavoriteSystemComponent* GetFavoriteSystemComponent() const; UFUNCTION(BlueprintPure) UGDMConsoleCommandValueProviderComponent* GetConsoleCommandValueProviderComponent() const; UFUNCTION(BlueprintPure) UGDMLocalizeStringComponent* GetLocalizeStringComponent() const; UFUNCTION(BlueprintPure) UGDMListenerComponent* GetListenerComponent() const; UFUNCTION(BlueprintPure, Category = "GDM") bool IsInitializedManager() const; protected: /** * マネージャーの初期化 */ virtual void OnInitializeManager(); /** * マネージャーの初期化処理が完了できる状態か判断する関数 * - false を返している間は初期化を完了しない * - true その後OnInitializeManagerBPが実行される */ UFUNCTION(BlueprintCallable, BlueprintNativeEvent, Category="GDM", meta=(DisplayName="IsReadyToInitializeManager", ScriptName="IsReadyToInitializeManager")) bool IsReadyToInitializeManager(); virtual bool IsReadyToInitializeManager_Implementation() { return true; } UFUNCTION(BlueprintImplementableEvent, Category="GDM", meta=(DisplayName="OnInitializeManager", ScriptName="OnInitializeManager")) void OnInitializeManagerBP(); /** * UIのルートWidgetの生成 */ virtual void CreateDebugMenuRootWidget(); /** * Navigation(無)有効(Menu開く前の状態にする) */ virtual void EnabledNavigationConfigs(); virtual void DisabledNavigationConfigs(); /** * マウスカーソルのオン・オフ(Menu開く前の状態にする) */ void EnableShowMouseCursorFlag(APlayerController* PlayerController); void RestoreShowMouseCursorFlag(APlayerController* PlayerController) const; /** * メニュー操作時のゲームポーズのオン・オフ(処理するかは「bGamePause」で判断) */ void TryEnableGamePause(); void RestoreGamePause() const; /** * スクショ処理終了後呼ばれる */ UFUNCTION() virtual void OnScreenshotRequestProcessed(); public: /** * コンソールコマンドの実行 */ virtual void ExecuteConsoleCommand(const FString& Command, APlayerController* PC); /** * DebugMenuの表示する */ virtual bool ShowDebugMenu(bool bWaitToCaptureBeforeOpeningMenuFlag = false); /** * DebugMenuを非表示にする */ virtual void HideDebugMenu(); /** * True:表示中 False:非表示 */ virtual bool IsShowingDebugMenu(); /** * オーナーのPlayerControllerを取得 */ UFUNCTION(BlueprintPure, Category = "GDM") APlayerController* GetOwnerPlayerController() const; /** * メインとなるWidget取得 */ virtual UGameDebugMenuRootWidget* GetDebugMenuRootWidget(); /** * 各Debug画面となるWidgetクラスのキーリストを取得 */ UFUNCTION(BlueprintCallable, Category = "GDM") int32 GetAllDebugMenuKeys(TArray& OutKeys); /** * キーと紐付いた各Debug画面となるWidgetクラスを取得 */ UFUNCTION(BlueprintCallable, Category = "GDM") TSubclassOf GetDebugMenuWidgetClass(const FString& Key); /** * 各Debug画面となるWidgetのインスタンスからキーを取得 */ UFUNCTION(BlueprintCallable, Category = "GDM") FString GetDebugMenuWidgetKey(const UGameDebugMenuWidget* Widget); /** * 生成済みのDebug画面となるWidgetを取得 */ UFUNCTION(BlueprintCallable, Category = "GDM") bool GetDebugMenuWidgetInstances(TArray& OutInstances); /** * ゲーム内のログ取得 */ virtual void GetOutputLogString(FString& OutLog, const FString& Separator); /** * ゲーム中実行したコンソールコマンドの履歴を取得 */ virtual void GetOutputCommandHistoryString(TArray& OutCommandHistory); UFUNCTION(BlueprintCallable, Category = "GDM") virtual void ClearCommandHistory(); virtual EGDMPropertyType GetPropertyType(const FProperty* TargetProperty) const; virtual bool RegisterObjectProperty(UObject* TargetObject, const FName PropertyName, const FGDMGameplayCategoryKey& CategoryKey, const FString& PropertySaveKey, const FText& DisplayPropertyName, const FText& Description, const FGDMPropertyUIConfigInfo& PropertyUIConfigInfo, const int32& DisplayPriority); virtual bool RegisterObjectFunction(UObject* TargetObject, const FName FunctionName, const FGDMGameplayCategoryKey& CategoryKey, const FString& FunctionSaveKey, const FText& DisplayFunctionName, const FText& Description, const int32& DisplayPriority); virtual UObject* GetObjectProperty(const int32 Index, FGDMGameplayCategoryKey& OutCategoryKey, FString& OutPropertySaveKey, FText& OutDisplayPropertyName, FText& OutDescription, FName& OutPropertyName, EGDMPropertyType& OutPropertyType, FString& OutEnumPathName, FGDMPropertyUIConfigInfo& OutPropertyUIConfigInfo); virtual void RemoveObjectProperty(const int32 Index); virtual UObject* GetObjectFunction(const int32 Index, FGDMGameplayCategoryKey& OutCategoryKey, FString& OutFunctionSaveKey, FText& OutDisplayFunctionName, FText& OutDescription, FName& OutFunctionName); void RemoveObjectFunction(const int32 Index); int32 GetNumObjectProperties() const; int32 GetNumObjectFunctions() const; UFUNCTION(BlueprintCallable, BlueprintPure=false) UObject* TryGetObjectProperty(const FString& InPropertySaveKey, const FString& InPropertyName, FGDMGameplayCategoryKey& OutCategoryKey, FText& OutDisplayPropertyName, FText& OutDescription, EGDMPropertyType& OutPropertyType, FString& OutEnumPathName, FGDMPropertyUIConfigInfo& OutPropertyUIConfigInfo) const; UFUNCTION(BlueprintCallable, BlueprintPure=false) UObject* TryGetObjectFunction(const FString& InFunctionSaveKey, const FString& InFunctionName, FGDMGameplayCategoryKey& OutCategoryKey, FText& OutDisplayFunctionName, FText& OutDescription) const; /** * ProxyComponentを対象PlayerControllerに追加する */ virtual void AddDebugMenuPCProxyComponent(APlayerController* PlayerController); /** * DebugMenuの入力無視フラグの設定 * (AControllerのSetIgnoreMoveInputと同様呼び出し回数を揃える必要がある) * * @param bNewInput true : DebugMenuの入力が無視されるようになる false : フラグを1つ解除する */ UFUNCTION(BlueprintCallable, Category = "GDM|Input") virtual void SetIgnoreInput(bool bNewInput); /** * DebugMenuの入力無視状態フラグをリセットする */ UFUNCTION(BlueprintCallable, Category = "GDM|Input") virtual void ResetIgnoreInput(); /** * DebugMenuの入力を無視する状態になっているか? * * @return True : 無視状態 False : 入力可能 */ UFUNCTION(BlueprintCallable, Category = "GDM|Input") virtual bool IsInputIgnored() const; /** * DebugMenuの言語を変更する */ UFUNCTION(BlueprintCallable, Category = "GDM") virtual void ChangeDebugMenuLanguage(FName LanguageKey, bool bForcedUpdate); /** * Viewport上にあるすべてのGameDebugMenuWidgetを取得する */ UFUNCTION(BlueprintCallable, Category = "GDM") TArray GetViewportDebugMenuWidgets(); UFUNCTION(BlueprintCallable, Category = "GDM") void ChangeConsoleCommandSetAsset(UGDMConsoleCommandSetAsset* NewCommandSetAsset); UFUNCTION(BlueprintPure, Category = "GDM") UGDMConsoleCommandSetAsset* GetConsoleCommandSetAsset() const; protected: virtual void CallExecuteConsoleCommandDispatcher(const FString& Command); virtual void CallShowDispatcher(); virtual void CallHideDispatcher(); UFUNCTION() virtual void OnWidgetAdded(UWidget* AddWidget, ULocalPlayer* Player); UFUNCTION() virtual void OnWidgetRemoved(UWidget* RemoveWidget); public: virtual void CallExecuteProcessEventDispatcher(const FName& FunctionName, UObject* TargetObject); virtual void CallChangePropertyBoolDispatcher(const FName& PropertyName, UObject* PropertyOwnerObject, bool New, bool Old, const FString& PropertySaveKey); virtual void CallChangePropertyIntDispatcher(const FName& PropertyName, UObject* PropertyOwnerObject, int32 New, int32 Old, const FString& PropertySaveKey); virtual void CallChangePropertyFloatDispatcher(const FName& PropertyName, UObject* PropertyOwnerObject, float New, float Old, const FString& PropertySaveKey); virtual void CallChangePropertyByteDispatcher(const FName& PropertyName, UObject* PropertyOwnerObject, uint8 New, uint8 Old, const FString& PropertySaveKey); virtual void CallChangePropertyStringDispatcher(const FName& PropertyName, UObject* PropertyOwnerObject, FString New, FString Old, const FString& PropertySaveKey); virtual void CallChangePropertyVectorDispatcher(const FName& PropertyName, UObject* PropertyOwnerObject, FVector New, FVector Old, const FString& PropertySaveKey); virtual void CallChangePropertyVector2DDispatcher(const FName& PropertyName, UObject* PropertyOwnerObject, FVector2D New, FVector2D Old, const FString& PropertySaveKey); virtual void CallChangePropertyRotatorDispatcher(const FName& PropertyName, UObject* PropertyOwnerObject, FRotator New, FRotator Old, const FString& PropertySaveKey); virtual void CallChangeDebugMenuLanguageDispatcher(const FName& NewLanguageKey, const FName& OldLanguageKey); virtual void CallStartScreenshotRequestDispatcher(); virtual void CallScreenshotRequestProcessedDispatcher(); virtual void CallLoadedDebugMenuDispatcher(); virtual void CallSavedDebugMenuDispatcher(); virtual void CallDeletedDebugMenuDispatcher(); }; ``` `SampleProject/Plugins/GameDebugMenu/Source/GameDebugMenu/Public/GameDebugMenuSettings.h`: ```h /** * Copyright (c) 2020 akihiko moroi * * This software is released under the MIT License. * (See accompanying file LICENSE.txt or copy at http://opensource.org/licenses/MIT) */ #pragma once #include "CoreMinimal.h" #include "Engine/DeveloperSettings.h" #include "Templates/SubclassOf.h" #include "GameDebugMenuTypes.h" #include "GameDebugMenuSettings.generated.h" class UGameDebugMenuMasterAsset; class AGameDebugMenuManager; class IGDMDeveloperSettingProvider; class AGDMDebugReportRequester; class UGDMEnhancedInputComponent; /** * DebugMenu用設定クラス */ UCLASS(config=GameDebugMenu, DefaultConfig) class GAMEDEBUGMENU_API UGameDebugMenuSettings : public UDeveloperSettings { GENERATED_BODY() public: /** マスターアセットのアセット名 */ UPROPERTY(EditAnywhere, config, Category = "Meta") FString MasterAssetName; /** コンソールコマンド名 */ UPROPERTY(EditAnywhere, config, Category = "ConsoleCommand") TArray ConsoleCommandNames; /** コンソールコマンドグループ名(同時に複数コマンド実行) */ UPROPERTY(EditAnywhere, config, Category = "ConsoleCommand") TArray ConsoleCommandGroups; /** コンソールコマンドペア名(トグルで2種のコマンド実行) */ UPROPERTY(EditAnywhere, config, Category = "ConsoleCommand") TArray ConsoleCommandPairs; /** コンソールコマンドナンバー(数値設定コマンド実行) */ UPROPERTY(EditAnywhere, config, Category = "ConsoleCommand") TArray ConsoleCommandNumbers; /** エディターでのみ追加されるコンソールコマンド名 */ UPROPERTY(EditAnywhere, config, Category = "ConsoleCommand") TArray EditorOnlyConsoleCommandNames; /** エディターでのみ追加されるコンソールコマンドグループ名 */ UPROPERTY(EditAnywhere, config, Category = "ConsoleCommand") TArray EditorOnlyConsoleCommandGroups; /** エディターでのみ追加されるコンソールコマンドペア名 */ UPROPERTY(EditAnywhere, config, Category = "ConsoleCommand") TArray EditorOnlyConsoleCommandPairs; /** エディターでのみ追加されるコンソールコマンドペア名 */ UPROPERTY(EditAnywhere, config, Category = "ConsoleCommand") TArray EditorOnlyConsoleCommandNumbers; /** コンソールコマンドのカテゴリ名表示順(最大255) */ UPROPERTY(EditAnywhere, config, Category = "ConsoleCommand") TArray OrderConsoleCommandCategoryTitles; /** Gameplayメニューのカテゴリ名表示順(最大255) */ UPROPERTY(EditAnywhere, config, Category = "Gameplay") TArray OrderGameplayCategoryTitles; /** DebugMenuのWidgetの入力優先度 */ UPROPERTY(EditAnywhere, config, Category = "Input") int32 WidgetInputActionPriority; /** バグレポート連携ツール */ UPROPERTY(EditAnywhere, config, Category = "ReportSettings") EGDMProjectManagementTool ProjectManagementToolType; /** Redmine連携用設定情報 */ UPROPERTY(EditAnywhere, config, Category = "ReportSettings") FGDMRedmineSettings RedmineSettings; /** Trello連携用設定情報 */ UPROPERTY(EditAnywhere, config, Category = "ReportSettings") FGDMTrelloSettings TrelloSettings; /** Jira連携用設定情報 */ UPROPERTY(EditAnywhere, config, Category = "ReportSettings") FGDMJiraSettings JiraSettings; /** バグレポート用の画面キャプチャ処理を無効化する */ UPROPERTY(EditAnywhere, Category = "ReportSettings") bool bDisableScreenCaptureProcessingWhenOpeningDebugMenu; /** DebugMenuから指定できるCultureのリスト */ UPROPERTY(EditAnywhere, config, Category = "Localization") TArray CultureList; /** デバックメニューの使用言語 */ UPROPERTY(EditAnywhere, config, Category = "Localization") FName DefaultGameDebugMenuLanguage; /** true: SaveGameを使用する。 false: Jsonファイルを使用する */ UPROPERTY(config, EditAnywhere, Category="Save") bool bUseSaveGame; /** JSON保存先のファイルパス(相対パス) */ UPROPERTY(config, EditAnywhere, Category="Save") FString SaveFilePath; /** JSON保存先のファイル名 */ UPROPERTY(config, EditAnywhere, Category="Save") FString SaveFileName; /** True: DebugMenuの保存機能を無効にする */ UPROPERTY(config, EditAnywhere, Category="Save") bool bDisableSaveFile; /** True: コンソールコマンドは保存されない False: 実行したコンソールコマンドはMaxCommandHistoryNumまで保存する(超えたら古いものから上書き) */ UPROPERTY(config, EditAnywhere, Category="Save") bool bDoesNotSaveConsoleCommand; /** 保存するコンソールコマンドの履歴件数 */ UPROPERTY(config, EditAnywhere, Category="Save") int32 MaxCommandHistoryNum; /** ここに含まれる文字のコンソールコマンドは保存されない */ UPROPERTY(config, EditAnywhere, Category="Save") TArray NoSaveConsoleCommands; /** DebugMenuでの改行文字 */ UPROPERTY(EditAnywhere, config, Category = "Other") FString LineBreakString; private: UPROPERTY(transient) mutable TObjectPtr MasterAsset; public: UGameDebugMenuSettings(); #if WITH_EDITOR virtual void PostEditChangeProperty(struct FPropertyChangedEvent& PropertyChangedEvent) override; virtual FText GetSectionText() const override; #endif TArray GetIssueCategoryNameList() const; TArray GetPriorityNameList() const; TArray GetAssigneeNameList() const; int32 GetDefaultIssueCategoryIndex() const; int32 GetDefaultPriorityIndex() const; FString GetGameplayCategoryTitle(const int32& ArrayIndex) const; int32 GetGameplayCategoryIndex(const int32& ArrayIndex) const; FString GetFullSavePath() const; const FGDMStringTableList* TryGetStringTableList(const FName& LanguageKey) const; TArray GetDebugMenuLanguageKeys() const; UClass* GetDebugMenuInputComponentClass() const; const TSubclassOf* GetDebugReportRequesterClass() const; FString GetDebugMenuString(const FName& LanguageKey, const FString& StringKey) const; UObject* GetDebugMenuFont() const; UGameDebugMenuMasterAsset* GetMasterAsset() const; private: void SetupCategoryResets(); void SetupCategorySlomo(); void SetupCategoryCamera(); void SetupCategoryProfiler(); void SetupCategoryDisplay(); void SetupCategoryShowDebug(); void SetupCategoryViewMode(); void SetupCategoryScalability(); void SetupCategoryFreeze(); void SetupCategoryDumpLogs(); void SetupCategoryNetwork(); void SetupCategorySound(); void SetupCategoryAbilitySystem(); void SetupCategoryOther(); void SetupCategoryLogVerbosity(); }; ``` `SampleProject/Plugins/GameDebugMenu/Source/GameDebugMenu/Public/GameDebugMenuTypes.h`: ```h /** * Copyright (c) 2020 akihiko moroi * * This software is released under the MIT License. * (See accompanying file LICENSE.txt or copy at http://opensource.org/licenses/MIT) */ #pragma once #include "CoreMinimal.h" #include "InputCoreTypes.h" #include "InputMappingContext.h" #include "Framework/Application/NavigationConfig.h" #include #include "GameDebugMenuTypes.generated.h" DECLARE_LOG_CATEGORY_EXTERN(LogGDM, Log, All); /** NavigationをきるためだけのConfig */ class FGDMNavigationConfig : public FNavigationConfig { public: FGDMNavigationConfig() :FNavigationConfig() { /** DebugMenuでは使用しない */ AnalogHorizontalKey = EKeys::Invalid; AnalogVerticalKey = EKeys::Invalid; KeyEventRules.Empty(); } virtual ~FGDMNavigationConfig() {} }; /** * */ UENUM(BlueprintType) enum class EGDMConsoleCommandType : uint8 { Non, Single, Group, Pair, Number, }; /** * メニュー選択後の動作 */ UENUM(BlueprintType) enum class EGDMConsoleCommandClickedEvent : uint8 { /** 特に何もしない */ Non, /** クリック後メニューを閉じる */ MenuClose, }; /** * プロパティの種類 */ UENUM(BlueprintType) enum class EGDMPropertyType : uint8 { GDM_Null, GDM_Bool, GDM_Int, GDM_Float, GDM_Enum, GDM_Byte, GDM_String, GDM_Vector, GDM_Vector2D, GDM_Rotator, }; /** * プロパティ編集時の最大値、最小値 */ USTRUCT(BlueprintType) struct GAMEDEBUGMENU_API FGDMPropertyRange { GENERATED_BODY() UPROPERTY(EditAnywhere, BlueprintReadWrite) bool bUseMin; UPROPERTY(EditAnywhere, BlueprintReadWrite) bool bUseMax; UPROPERTY(EditAnywhere, BlueprintReadWrite) float MinValue; UPROPERTY(EditAnywhere, BlueprintReadWrite) float MaxValue; FGDMPropertyRange() : bUseMin(false) , bUseMax(false) , MinValue(0.0f) , MaxValue(0.0f) { } }; /** * プロパティ編集時のUI設定情報 */ USTRUCT(BlueprintType) struct GAMEDEBUGMENU_API FGDMPropertyUIConfigInfo { GENERATED_BODY() /** 設定範囲 */ UPROPERTY(EditAnywhere, BlueprintReadWrite) FGDMPropertyRange Range; /** 値の変化量 */ UPROPERTY(EditAnywhere, BlueprintReadWrite) float DefaultChangeAmount; /** 最大変化量 */ UPROPERTY(EditAnywhere, BlueprintReadWrite) float MaxChangeAmount; /** 最大変化量になるまでの時間 */ UPROPERTY(EditAnywhere, BlueprintReadWrite) float MaxChangeAmountTime; FGDMPropertyUIConfigInfo() : Range() , DefaultChangeAmount(1.0f) , MaxChangeAmount(1.0f) , MaxChangeAmountTime(1.3f) { } }; /** * コマンドでの実行範囲 */ UENUM(BlueprintType) enum class EGDMConsoleCommandNetType : uint8 { /** ローカルでのみ実行する */ LocalOnly, /** Serverで全プレイヤーに実行させる */ ServerAll, }; /** * UIでコンソールコマンドを操作するのに使用する情報 */ USTRUCT(BlueprintType) struct GAMEDEBUGMENU_API FGDMConsoleCommand { GENERATED_BODY() UPROPERTY(BlueprintReadWrite) EGDMConsoleCommandType Type; UPROPERTY(EditAnywhere,BlueprintReadWrite,config) EGDMConsoleCommandClickedEvent ClickedEvent; UPROPERTY(EditAnywhere, BlueprintReadWrite, config) uint8 CategoryIndex; UPROPERTY(EditAnywhere, BlueprintReadWrite, config) FText Title; UPROPERTY(EditAnywhere, BlueprintReadWrite, config) FText Description; UPROPERTY(EditAnywhere, BlueprintReadWrite, config) EGDMConsoleCommandNetType CommandNetType; FGDMConsoleCommand(); virtual ~FGDMConsoleCommand() = default; virtual FString BuildCommandIdentifier() const PURE_VIRTUAL(FGDMConsoleCommand::GetCommandId, return FString();); }; /** * UIで実行するときに単体で実行する */ USTRUCT(BlueprintType) struct GAMEDEBUGMENU_API FGDMConsoleCommandSingle : public FGDMConsoleCommand { GENERATED_BODY() UPROPERTY(EditAnywhere, BlueprintReadWrite, config) FString ConsoleCommandName; FGDMConsoleCommandSingle(); virtual FString BuildCommandIdentifier() const override; }; /** * UIで実行するときに同時に実行できるようにするためのグループ */ USTRUCT(BlueprintType) struct GAMEDEBUGMENU_API FGDMConsoleCommandGroup : public FGDMConsoleCommand { GENERATED_BODY() UPROPERTY(EditAnywhere, BlueprintReadWrite, config) TArray ConsoleCommandNames; FGDMConsoleCommandGroup(); virtual FString BuildCommandIdentifier() const override; }; /** * UIでトグル操作するときのペアになるコマンド */ USTRUCT(BlueprintType) struct GAMEDEBUGMENU_API FGDMConsoleCommandPair : public FGDMConsoleCommand { GENERATED_BODY() UPROPERTY(EditAnywhere, BlueprintReadWrite, config) FString FirstConsoleCommandName; UPROPERTY(EditAnywhere, BlueprintReadWrite, config) FString SecondConsoleCommandName; FGDMConsoleCommandPair(); virtual FString BuildCommandIdentifier() const override; }; /** * UIで実行するときに数字指定で実行する */ USTRUCT(BlueprintType) struct GAMEDEBUGMENU_API FGDMConsoleCommandNumber : public FGDMConsoleCommand { GENERATED_BODY() UPROPERTY(EditAnywhere, BlueprintReadWrite, config) FString ConsoleCommandName; UPROPERTY(EditAnywhere, BlueprintReadWrite, config) FString PreConsoleCommandName; UPROPERTY(EditAnywhere, BlueprintReadWrite, config) FString PostConsoleCommandName; UPROPERTY(EditAnywhere, BlueprintReadWrite, config) FGDMPropertyUIConfigInfo UIConfigInfo; UPROPERTY(EditAnywhere, BlueprintReadWrite, config) float DefaultValue; /** コマンドの値を取得するときに使用するコンソール名 */ UPROPERTY(EditAnywhere, BlueprintReadWrite, config) FString ConsoleVariableName; FGDMConsoleCommandNumber(); virtual FString BuildCommandIdentifier() const override; }; /** * */ USTRUCT(BlueprintType) struct GAMEDEBUGMENU_API FGDMMenuCategoryKey { GENERATED_USTRUCT_BODY() UPROPERTY(EditAnywhere, BlueprintReadWrite) uint8 Index; UPROPERTY(EditAnywhere, BlueprintReadWrite) FString KeyName; public: FGDMMenuCategoryKey(uint8 InIndex, FString InKeyName); FGDMMenuCategoryKey(uint8 InIndex); FGDMMenuCategoryKey(); bool operator==(FGDMMenuCategoryKey& InOther); bool operator!=(FGDMMenuCategoryKey& InOther); bool operator<(FGDMMenuCategoryKey& InOther); bool operator>(FGDMMenuCategoryKey& InOther); FORCEINLINE operator uint8() const { return Index; } }; USTRUCT(BlueprintType) struct GAMEDEBUGMENU_API FGDMGameplayCategoryKey : public FGDMMenuCategoryKey { GENERATED_USTRUCT_BODY() public: FGDMGameplayCategoryKey(uint8 InIndex, FString InKeyName); FGDMGameplayCategoryKey(uint8 InIndex); FGDMGameplayCategoryKey(); }; /** * Menuに登録できるプロパティ情報 */ class GAMEDEBUGMENU_API FGDMObjectPropertyInfo { public: FGDMGameplayCategoryKey CategoryKey; FText Name; FText Description; TWeakObjectPtr TargetObject; FProperty* TargetProperty; FName PropertyName; TWeakObjectPtr EnumType; FGDMPropertyUIConfigInfo ConfigInfo; int32 DisplayPriority; UScriptStruct* Struct; FString PropertySaveKey; FGDMObjectPropertyInfo() : CategoryKey() , Name(FText::GetEmpty()) , Description(FText::GetEmpty()) , TargetObject(nullptr) , TargetProperty(nullptr) , PropertyName(NAME_None) , EnumType(nullptr) , ConfigInfo() , DisplayPriority(0) , Struct(nullptr) , PropertySaveKey() { } }; /** * Menuに登録できる関数情報 */ class GAMEDEBUGMENU_API FGDMObjectFunctionInfo { public: FGDMGameplayCategoryKey CategoryKey; FText Name; FText Description; TWeakObjectPtr TargetObject; TWeakObjectPtr TargetFunction; FName FunctionName; int32 DisplayPriority; FString FunctionSaveKey; FGDMObjectFunctionInfo() : CategoryKey() , Name(FText::GetEmpty()) , Description(FText::GetEmpty()) , TargetObject(nullptr) , TargetFunction(nullptr) , FunctionName(NAME_None) , DisplayPriority(0) , FunctionSaveKey() { } }; /** * 未登録状態のオブジェクト情報 */ struct GAMEDEBUGMENU_API FGDMPendingObjectData { TWeakObjectPtr TargetObject; FName TargetName; FGDMGameplayCategoryKey CategoryKey; FText DisplayPropertyName; FText Description; FGDMPropertyUIConfigInfo ConfigInfo; int32 DisplayPriority; FString SaveKey; FGDMPendingObjectData() : TargetObject(nullptr) , TargetName(NAME_None) , CategoryKey() , DisplayPropertyName(FText::GetEmpty()) , Description(FText::GetEmpty()) , ConfigInfo() , DisplayPriority(0) , SaveKey() { } }; /** * */ UENUM(BlueprintType) enum class EGDMProjectManagementTool : uint8 { Redmine, Trello, Jira, }; USTRUCT(BlueprintType) struct GAMEDEBUGMENU_API FGDMProjectManagementToolSettings { GENERATED_BODY() /** 生成したAPIキー */ UPROPERTY(EditAnywhere, BlueprintReadWrite, config) FString AccessKey; FGDMProjectManagementToolSettings() :AccessKey() { } }; /** * Redmine連携用の情報 */ USTRUCT(BlueprintType) struct GAMEDEBUGMENU_API FGDMRedmineSettings : public FGDMProjectManagementToolSettings { GENERATED_BODY() /** 接続先ホスト名 */ UPROPERTY(EditAnywhere,BlueprintReadWrite,config) FString HostName; /** プロジェクト識別ID */ UPROPERTY(EditAnywhere, BlueprintReadWrite, config, meta = (UIMin = 1, ClampMin = 1)) int32 ProjectId; /** トラッカー名 */ UPROPERTY(EditAnywhere, BlueprintReadWrite, config) TArray TrackerNameList; /** トラッカー初期値 */ UPROPERTY(EditAnywhere, BlueprintReadWrite, config, meta = (UIMin = 1, ClampMin = 1)) int32 DefaultTrackerIndex; /** 優先度名 */ UPROPERTY(EditAnywhere, BlueprintReadWrite, config) TArray PriorityNameList; /** 優先度初期値 */ UPROPERTY(EditAnywhere, BlueprintReadWrite, config, meta = (UIMin = 1, ClampMin = 1)) int32 DefaultPriorityIndex; FGDMRedmineSettings() : Super() , HostName(TEXT("localhost")) , ProjectId(1) , TrackerNameList() , DefaultTrackerIndex(0) , PriorityNameList() , DefaultPriorityIndex(1) { TrackerNameList.Reset(); TrackerNameList.Add(FText::FromString(TEXT("バグ"))); TrackerNameList.Add(FText::FromString(TEXT("機能"))); TrackerNameList.Add(FText::FromString(TEXT("サポート"))); PriorityNameList.Reset(); PriorityNameList.Add(FText::FromString(TEXT("低め"))); PriorityNameList.Add(FText::FromString(TEXT("通常"))); PriorityNameList.Add(FText::FromString(TEXT("高め"))); PriorityNameList.Add(FText::FromString(TEXT("急いで"))); PriorityNameList.Add(FText::FromString(TEXT("今すぐ"))); } }; /** * Trello連携用の情報 */ USTRUCT(BlueprintType) struct GAMEDEBUGMENU_API FGDMTrelloSettings : public FGDMProjectManagementToolSettings { GENERATED_BODY() /** Trello用に生成したトークン */ UPROPERTY(EditAnywhere, BlueprintReadWrite, config) FString AccessToken; /** 追加先リストID */ UPROPERTY(EditAnywhere, BlueprintReadWrite, config) TArray CardListIDs; /** 追加先リスト名 */ UPROPERTY(EditAnywhere, BlueprintReadWrite, config) TArray CardListNames; FGDMTrelloSettings() : Super() , AccessToken() , CardListIDs() , CardListNames() { CardListNames.Add(FText::FromString(TEXT("Trello追加先リスト名"))); } }; /** * Jira連携用の情報 */ USTRUCT(BlueprintType) struct GAMEDEBUGMENU_API FGDMJiraSettings : public FGDMProjectManagementToolSettings { GENERATED_BODY() /** 接続先ホスト名 */ UPROPERTY(EditAnywhere, BlueprintReadWrite, config) FString HostName; /** プロジェクト識別KEY名 */ UPROPERTY(EditAnywhere, BlueprintReadWrite, config) FString ProjectKeyName; /** ユーザー名 */ UPROPERTY(EditAnywhere, BlueprintReadWrite, config) FString UserName; /** 課題の種類 */ UPROPERTY(EditAnywhere, BlueprintReadWrite, config) TArray IssueTypeList; /** 課題の種類初期値 */ UPROPERTY(EditAnywhere, BlueprintReadWrite, config) int32 DefaultIssueTypeIndex; /** 優先度名 */ UPROPERTY(EditAnywhere, BlueprintReadWrite, config) TArray PriorityNameList; /** 優先度初期値 */ UPROPERTY(EditAnywhere, BlueprintReadWrite, config) int32 DefaultPriorityIndex; /** 担当者名リスト(Key: accountId Value: 名前) */ UPROPERTY(EditAnywhere, BlueprintReadWrite, config) TMap AssigneeList; UPROPERTY(EditAnywhere,BlueprintReadWrite,config) TArray LabelNameList; FGDMJiraSettings() : Super() , HostName(TEXT("localhost")) , ProjectKeyName() , UserName() , IssueTypeList() , DefaultIssueTypeIndex(1) , PriorityNameList() , DefaultPriorityIndex(2) , AssigneeList() , LabelNameList() { IssueTypeList.Reset(); IssueTypeList.Add(FText::FromString(TEXT("タスク"))); IssueTypeList.Add(FText::FromString(TEXT("バグ"))); IssueTypeList.Add(FText::FromString(TEXT("改善"))); PriorityNameList.Reset(); PriorityNameList.Add(FText::FromString(TEXT("Highest"))); PriorityNameList.Add(FText::FromString(TEXT("High"))); PriorityNameList.Add(FText::FromString(TEXT("Medium"))); PriorityNameList.Add(FText::FromString(TEXT("Low"))); PriorityNameList.Add(FText::FromString(TEXT("Lowest"))); } FString GetAssigneeAccountIdByListIndex(int32 ListIndex) const; FText GetAssigneeTextByListIndex(int32 ListIndex) const; }; /** * DebugMenuの文字用のStringTableのリスト */ USTRUCT(BlueprintType) struct GAMEDEBUGMENU_API FGDMStringTableList { GENERATED_BODY() UPROPERTY(EditAnywhere, BlueprintReadWrite) TArray> StringTables; FGDMStringTableList() :StringTables() { } }; /** * リスト表示メニューのカテゴリ名 */ USTRUCT(BlueprintType) struct GAMEDEBUGMENU_API FGDMOrderMenuCategoryTitle { GENERATED_BODY() UPROPERTY(EditAnywhere) FString Title; UPROPERTY(VisibleAnywhere) int32 Index; #if WITH_EDITORONLY_DATA UPROPERTY(VisibleAnywhere) FString PreviewTitle; #endif FGDMOrderMenuCategoryTitle() : Title() , Index(INDEX_NONE) #if WITH_EDITORONLY_DATA , PreviewTitle() #endif { } FGDMOrderMenuCategoryTitle(FString InTitle) : Title(InTitle) , Index(INDEX_NONE) #if WITH_EDITORONLY_DATA , PreviewTitle() #endif { } FGDMOrderMenuCategoryTitle(FString InTitle, int32 InIndex) : Title(InTitle) , Index(InIndex) #if WITH_EDITORONLY_DATA , PreviewTitle() #endif { } FGDMOrderMenuCategoryTitle(FString InTitle, FString InPreviewTitle) : Title(InTitle) , Index(INDEX_NONE) #if WITH_EDITORONLY_DATA , PreviewTitle(InPreviewTitle) #endif { } }; /** * */ USTRUCT(BlueprintType) struct FGameDebugMenuWidgetInputMappingContextData { GENERATED_BODY() UPROPERTY(Editanywhere, BlueprintReadWrite) TObjectPtr InputMappingContext = nullptr; UPROPERTY(Editanywhere, BlueprintReadWrite) int32 Priority = 1000000; }; /** * お気に入り項目の登録情報 */ USTRUCT(BlueprintType) struct FGDMFavoriteEntry { GENERATED_BODY() UPROPERTY(EditAnywhere, BlueprintReadWrite) FString DefinitionName; UPROPERTY(EditAnywhere, BlueprintReadWrite) FString SaveKey; }; ``` `SampleProject/Plugins/GameDebugMenu/Source/GameDebugMenu/Public/Input/GDMDebugCameraInput.h`: ```h /** * Copyright (c) 2020 akihiko moroi * * This software is released under the MIT License. * (See accompanying file LICENSE.txt or copy at http://opensource.org/licenses/MIT) */ #pragma once #include "CoreMinimal.h" #include "GameFramework/Actor.h" #include "GDMDebugCameraInput.generated.h" class ADebugCameraController; class AGameDebugMenuManager; /** * DebugCameraController動作中に処理される入力判定を制御するクラス * 「ToogleDebugCamera」実行時に別途、入力機能(メニューの開閉など)をここで実装できるようにしてる * (BP_GDMDebugCameraInput参照) */ UCLASS(Blueprintable, NotBlueprintType, notplaceable) class GAMEDEBUGMENU_API AGDMDebugCameraInput : public AActor { GENERATED_BODY() FDelegateHandle ActorSpawnedDelegateHandle; TWeakObjectPtr DebugCameraController; public: AGDMDebugCameraInput(); public: UFUNCTION(BlueprintPure) AGameDebugMenuManager* GetOwnerGameDebugMenuManager() const; UFUNCTION(BlueprintPure) ADebugCameraController* GetDebugCameraController() const; void SetDebugCameraController(ADebugCameraController* DCC); UFUNCTION(BlueprintCallable) virtual void ToggleOrbitHitPoint(); UFUNCTION(BlueprintCallable) virtual void PawnTeleport(); }; ``` `SampleProject/Plugins/GameDebugMenu/Source/GameDebugMenu/Public/Input/GDMEnhancedInputComponent.h`: ```h /** * Copyright (c) 2020 akihiko moroi * * This software is released under the MIT License. * (See accompanying file LICENSE.txt or copy at http://opensource.org/licenses/MIT) */ #pragma once #include "CoreMinimal.h" #include "EnhancedInputComponent.h" #include "GDMEnhancedInputComponent.generated.h" /** * DebugMenu用のUIで入力判定を制御する専用のEnhancedInputComponent */ UCLASS() class GAMEDEBUGMENU_API UGDMEnhancedInputComponent : public UEnhancedInputComponent { GENERATED_BODY() public: UGDMEnhancedInputComponent(); virtual bool CanProcessInputAction(const UInputAction* Action) const; FEnhancedInputActionEventBinding& BindDebugMenuAction(const UInputAction* Action, ETriggerEvent TriggerEvent, UObject* Object, FName FunctionName); FEnhancedInputActionEventBinding& BindDebugMenuAction(const UInputAction* Action, ETriggerEvent TriggerEvent, TFunction&& Callback); }; ``` `SampleProject/Plugins/GameDebugMenu/Source/GameDebugMenu/Public/Input/GDMInputSystemComponent.h`: ```h /** * Copyright (c) 2020 akihiko moroi * * This software is released under the MIT License. * (See accompanying file LICENSE.txt or copy at http://opensource.org/licenses/MIT) */ #pragma once #include "CoreMinimal.h" #include "Data/GameDebugMenuManagerAsset.h" #include "GDMInputSystemComponent.generated.h" class ADebugCameraController; class UGDMEnhancedInputComponent; class AGameDebugMenuManager; class AGDMDebugCameraInput; /** * DebugMenuでの入力処理を管理するコンポーネント */ UCLASS() class GAMEDEBUGMENU_API UGDMInputSystemComponent : public UActorComponent { GENERATED_BODY() protected: /** DebugMenuの入力を無視するフラグカウント */ uint16 IgnoreDebugMenuInput; /** デバックカメラ用のインプットアクターのインスタンス */ UPROPERTY(Transient) TObjectPtr DebugCameraInput; /** 登録中のInputComponent郡 */ TMap>> RegisteredInputGroups; /** 利用中のInputComponent郡 */ TMap>> ActiveInputStacks; /** 現在アクティブなInputComponentのグループ名 */ FName CurrentInputGroupName; /** メニューの開閉状態の識別フラグ */ UPROPERTY(Transient) bool bMenuOpen; /** ルートのWidgetが持つInputComponent */ UPROPERTY(Transient) TObjectPtr RootWidgetInputComponent; /** DebugMenuManagerが作られたときに追加される InputMappingContext */ UPROPERTY(Transient) TArray AddInputMappingContextWhenCreateManager; /** DebugMenuを表示するときに追加される InputMappingContext */ UPROPERTY(Transient) TArray AddInputMappingContextWhenDebugMenuIsShow; /** ADebugCameraController生成チェック用のハンドル */ FDelegateHandle ActorSpawnedDelegateHandle; TWeakObjectPtr DebugCameraController; public: /** 入力判定用ログ */ UPROPERTY(BlueprintReadWrite, Category = "GDM|Debug") bool bOutputDebugLog; public: UGDMInputSystemComponent(); virtual void TickComponent(float DeltaTime, enum ELevelTick TickType, FActorComponentTickFunction* ThisTickFunction) override; virtual void EndPlay(const EEndPlayReason::Type EndPlayReason) override; public: virtual void SetIgnoreInput(bool bNewInput); virtual void ResetIgnoreInput(); virtual bool IsInputIgnored() const; /** * 初期化 */ virtual void Initialize(UGameDebugMenuManagerAsset* MenuDataAsset); /** * 現在アクティブなグループにInputComponentを登録/解除をする */ virtual void RegisterInputComponent(UInputComponent* InputComponent); virtual void UnregisterInputComponent(UInputComponent* InputComponent); /** * 指定したグループにInputComponentを登録/解除をする */ virtual void RegisterInputComponentToGroup(UInputComponent* InputComponent, const FName GroupName); virtual void UnregisterInputComponentFromGroup(UInputComponent* InputComponent, const FName GroupName); /** * 入力グループを変更する * @param NewGroupName - 新しいグループ名 */ virtual void SwitchToInputGroup(const FName NewGroupName); /** * デバックメニューが開くと呼ばれる * @note アクティブなメニューのInputComponentをPlayerControllerに追加する */ virtual void OnOpenMenu(); /** * デバックメニューが閉じると呼ばれる * @note アクティブなメニューのInputComponentをPlayerControllerに削除する */ virtual void OnCloseMenu(); protected: virtual void CreateDebugCameraInputClass(TSubclassOf DebugCameraInputClass); AGameDebugMenuManager* GetOwnerGameDebugMenuManager() const; TArray GetPlayerControllers() const; virtual void OnActorSpawned(AActor* SpawnActor); }; ``` `SampleProject/Plugins/GameDebugMenu/Source/GameDebugMenu/Public/Input/GDMInputTriggerPulseWithDelay.h`: ```h /** * Copyright (c) 2020 akihiko moroi * * This software is released under the MIT License. * (See accompanying file LICENSE.txt or copy at http://opensource.org/licenses/MIT) */ #pragma once #include "CoreMinimal.h" #include "InputTriggers.h" #include "GDMInputTriggerPulseWithDelay.generated.h" /** * 最初と2回目以降のトリガー時間を別に指定できるInputTrigger */ UCLASS(NotBlueprintable, meta = (DisplayName = "GDMPulseWithDelay")) class GAMEDEBUGMENU_API UGDMInputTriggerPulseWithDelay : public UInputTrigger/* UInputTriggerTimedBaseのENHANCEDINPUT_APIがなぜかUE5.6で削除された.... */ { GENERATED_BODY() public: /** 押した瞬間にも発火させるか */ UPROPERTY(EditAnywhere, Config, BlueprintReadWrite, Category = "Trigger Settings") bool bTriggerOnStart = true; /** 最初の1回目を発火するまでの遅延(秒) */ UPROPERTY(EditAnywhere, Config, BlueprintReadWrite, Category = "Trigger Settings", meta = (ClampMin = "0")) float InitialDelay = 0.5f; /** 2回目以降のトリガー間隔(秒) */ UPROPERTY(EditAnywhere, Config, BlueprintReadWrite, Category = "Trigger Settings", meta = (ClampMin = "0")) float RepeatInterval = 0.1f; /** 入力が保持されている間の最大トリガー回数(0 = 無制限) */ UPROPERTY(EditAnywhere, Config, BlueprintReadWrite, Category = "Trigger Settings", meta = (ClampMin = "0")) int32 TriggerLimit = 0; // How long have we been actuating this trigger? UPROPERTY(BlueprintReadWrite, Category = "Trigger Settings") float HeldDuration = 0.0f; /** * Should global time dilation be applied to the held duration? * Default is set to false. * * If this is set to true, then the owning Player Controller's actor time dilation * will be used when calculating the HeldDuration. * * @see UInputTriggerTimedBase::CalculateHeldDuration * @see AWorldSettings::GetEffectiveTimeDilation */ UPROPERTY(EditAnywhere, Config, BlueprintReadWrite, Category = "Trigger Settings") bool bAffectedByTimeDilation = false; private: bool bWasPressed = false; bool bTriggeredOnStart = false; int32 TriggerCount = 0; public: virtual FString GetDebugState() const override; virtual ETriggerEventsSupported GetSupportedTriggerEvents() const override { return ETriggerEventsSupported::Ongoing; } virtual ETriggerState UpdateState_Implementation(const UEnhancedPlayerInput* PlayerInput, FInputActionValue ModifiedValue, float DeltaTime) override; float CalculateHeldDuration(const UEnhancedPlayerInput* PlayerInput, float DeltaTime) const; }; ``` `SampleProject/Plugins/GameDebugMenu/Source/GameDebugMenu/Public/Input/GDMPadInputWidgetController.h`: ```h /** * Copyright (c) 2020 akihiko moroi * * This software is released under the MIT License. * (See accompanying file LICENSE.txt or copy at http://opensource.org/licenses/MIT) */ #pragma once #include "CoreMinimal.h" #include "Components/Widget.h" #include "GDMPadInputWidgetController.generated.h" class UGDMListenerComponent; class UGameDebugMenuWidget; /** * DebugMenuの中で、パッド操作などによって「選択状態」の Widget を制御するためのクラス * 主にリスト表記のUIでの選択位置や見た目の更新を行うためのインターフェースを提供します * 詳細はBP_GDM_PadInputWidgetController,WB_GDM_ReportMenu,WB_GDM_FavoriteMenu参照 */ UCLASS(Blueprintable) class GAMEDEBUGMENU_API UGDMPadInputWidgetController : public UObject { GENERATED_BODY() protected: UPROPERTY(BlueprintReadOnly, meta = (ExposeOnSpawn)) UGameDebugMenuWidget* OwnerGameDebugMenuWidget; public: virtual UWorld* GetWorld() const override; /** * 次のWidgetを「選択中」にする * @return 現在の選択中インデックスを返す */ UFUNCTION(BlueprintCallable, BlueprintImplementableEvent, Category = "GDM") int32 NextChoosing(); /** * 1つ前のWidgetを「選択中」にする * @return 現在の選択中インデックスを返す */ UFUNCTION(BlueprintCallable, BlueprintImplementableEvent, Category = "GDM") int32 PreviousChoosing(); /** * 現在「選択中」のWidgetを取得する */ UFUNCTION(BlueprintCallable, BlueprintImplementableEvent, Category = "GDM") UWidget* GetChoosingWidget() const; /** * 現在「選択中」のWidgetのインデックスを取得する */ UFUNCTION(BlueprintCallable, BlueprintImplementableEvent, Category = "GDM") int32 GetChoosingWidgetIndex() const; /** * 現在「選択した」のWidgetを取得する */ UFUNCTION(BlueprintCallable, BlueprintImplementableEvent, Category = "GDM") UWidget* GetChosenWidget() const; /** * 現在「選択した」のWidgetのインデックスを取得する */ UFUNCTION(BlueprintCallable, BlueprintImplementableEvent, Category = "GDM") int32 GetChosenWidgetIndex() const; /** * 選択が確定された状態かどうかを返す * @return true: 選択した状態 false: 選択中 */ UFUNCTION(BlueprintCallable, BlueprintImplementableEvent, Category = "GDM") bool IsChosen() const; /** * 選択状態を「選択確定(Chosen)」に切り替える */ UFUNCTION(BlueprintCallable, BlueprintImplementableEvent, Category = "GDM") void ChangeChosenMode(); /** * 選択状態を「選択中(Choosing)」に切り替える */ UFUNCTION(BlueprintCallable, BlueprintImplementableEvent, Category = "GDM") void ChangeChoosingMode(); /** * 新しく「選択中」とする Widget を直接指定 */ UFUNCTION(BlueprintCallable, BlueprintImplementableEvent, Category = "GDM") void SetChoosingWidget(UWidget* NewChoosingWidget); /** * 指定した UWidget が内部で管理していれていれば、そのインデックスを返す * @param TargetWidget - 対象のWidget * @return あれば 0 以上,なければ -1を返す */ UFUNCTION(BlueprintCallable, BlueprintPure=false, BlueprintImplementableEvent, Category = "GDM") int32 GetWidgetIndexByWidget(UWidget* TargetWidget) const; /** * 内部で管理している Widget 群の中から、インデックスに対応する Widget を取得 * @param Index - 対象のインデックス * @return 無効なインデックスなら nullptr を返す */ UFUNCTION(BlueprintCallable, BlueprintPure=false, BlueprintImplementableEvent, Category = "GDM") UWidget* GetWidgetByIndex(int32 Index) const; /** * 管理してるWidgetの数を取得 */ UFUNCTION(BlueprintCallable, BlueprintImplementableEvent, Category = "GDM") int32 GetWidgetCount() const; }; ``` `SampleProject/Plugins/GameDebugMenu/Source/GameDebugMenu/Public/Log/GDMOutputDevice.h`: ```h /** * Copyright (c) 2020 akihiko moroi * * This software is released under the MIT License. * (See accompanying file LICENSE.txt or copy at http://opensource.org/licenses/MIT) */ #pragma once #include "CoreMinimal.h" #include "UObject/NoExportTypes.h" #include "Misc/OutputDevice.h" #include class AGameDebugMenuManager; /** * DebugMenuで使用できるようにするOutputLogの文字情報 * プロジェクト名.log取得したかったけど起動中はアクセスできないため文字列情報はこれから取得。 * ただAGameDebugMenuManagerが生成されてから動作するので正確には同じではない */ class GAMEDEBUGMENU_API FGDMOutputDevice : public FOutputDevice { TArray Logs; TArray CommandHistory; mutable FCriticalSection CommandHistoryMutex; public: FGDMOutputDevice(); virtual ~FGDMOutputDevice() override; virtual void Serialize(const TCHAR* Data, ELogVerbosity::Type Verbosity, const class FName& Category, const double Time) override; virtual void Serialize(const TCHAR* Data, ELogVerbosity::Type Verbosity, const class FName& Category) override; public: TArray GetLogs() const; TArray GetCommandHistory() const; void ClearCommandHistory(); }; ``` `SampleProject/Plugins/GameDebugMenu/Source/GameDebugMenu/Public/Reports/GDMDebugReportRequester.h`: ```h /** * Copyright (c) 2020 akihiko moroi * * This software is released under the MIT License. * (See accompanying file LICENSE.txt or copy at http://opensource.org/licenses/MIT) */ #pragma once #include "CoreMinimal.h" #include "GameFramework/Actor.h" #include "WebImage.h" #include "GDMDebugReportRequester.generated.h" class AGameDebugMenuManager; /** * バグレポート送信処理を行うクラス */ UCLASS(notplaceable) class GAMEDEBUGMENU_API AGDMDebugReportRequester : public AActor { GENERATED_BODY() public: static const FString LineBreak; bool bSendLogs; bool bSendScreenshotCapture; FString Subject; FString Description; int32 IssueCategoryIndex; int32 PriorityIndex; int32 AssigneeIndex; TArray ScreenshotImageData; FDateTime ScreenshotCapturedDateTime; int32 TestCount; int32 MaxTestCount; bool bWasRequestSuccessful; public: AGDMDebugReportRequester(); public: virtual void StartRequest(); protected: virtual void SuccessRequest(); virtual void FailedRequest(); AGameDebugMenuManager* GetOwnerDebugMenuManager() const; FString GetSubject(); FString GetDescription(); UFUNCTION(BlueprintNativeEvent) FString PrefixSubjectString(); UFUNCTION(BlueprintNativeEvent) FString PrefixDescriptionString(); UFUNCTION(BlueprintNativeEvent) FString SuffixSubjectString(); UFUNCTION(BlueprintNativeEvent) FString SuffixDescriptionString(); /* multipart/form-data形式で送信する場合使用 */ int32 GetUTF8StringSize(const FString& Text); FString MakeBoundaryString(); void AddContentString(const FString& BoundaryKey, const FString& DataName, const FString& ContentType, const FString& SourceData, TArray& OutSendData); void AddContent(const FString& BoundaryKey, const FString& DataName, const FString& ContentType, const TArray& SourceData, TArray& OutSendData); void AddEndContentString(const FString& BoundaryKey, TArray& OutSendData); }; ``` `SampleProject/Plugins/GameDebugMenu/Source/GameDebugMenu/Public/Reports/GDMRequesterJira.h`: ```h /** * Copyright (c) 2020 akihiko moroi * * This software is released under the MIT License. * (See accompanying file LICENSE.txt or copy at http://opensource.org/licenses/MIT) */ #pragma once #include "CoreMinimal.h" #include "Reports/GDMDebugReportRequester.h" #include "GDMRequesterJira.generated.h" /** * Jiraへのバグレポート送信処理 */ UCLASS() class GAMEDEBUGMENU_API AGDMRequesterJira : public AGDMDebugReportRequester { GENERATED_BODY() public: virtual void StartRequest() override; protected: virtual void RequestUploadContent(const FString& IssueKey); virtual void OnResponseReceived(FHttpRequestPtr Request, FHttpResponsePtr Response, bool bWasSuccessful); virtual void OnResponseReceivedUploaded(FHttpRequestPtr Request, FHttpResponsePtr Response, bool bWasSuccessful); }; ``` `SampleProject/Plugins/GameDebugMenu/Source/GameDebugMenu/Public/Reports/GDMRequesterRedmine.h`: ```h /** * Copyright (c) 2020 akihiko moroi * * This software is released under the MIT License. * (See accompanying file LICENSE.txt or copy at http://opensource.org/licenses/MIT) */ #pragma once #include "CoreMinimal.h" #include "Reports/GDMDebugReportRequester.h" #include "GDMRequesterRedmine.generated.h" /** * Redmineへのバグレポート送信処理 */ UCLASS() class GAMEDEBUGMENU_API AGDMRequesterRedmine : public AGDMDebugReportRequester { GENERATED_BODY() FString TokenScreenshotCapture; FString TokenLog; public: virtual void StartRequest() override; protected: virtual void RequestUploadScreenshotCapture(); virtual void RequestUploadLog(); virtual void RequestIssues(); virtual void OnResponseReceived(FHttpRequestPtr Request, FHttpResponsePtr Response, bool bWasSuccessful); virtual void OnResponseReceivedUploaded(FHttpRequestPtr Request, FHttpResponsePtr Response, bool bWasSuccessful); virtual void OnResponseReceivedLog(FHttpRequestPtr Request, FHttpResponsePtr Response, bool bWasSuccessful); }; ``` `SampleProject/Plugins/GameDebugMenu/Source/GameDebugMenu/Public/Reports/GDMRequesterTrello.h`: ```h /** * Copyright (c) 2020 akihiko moroi * * This software is released under the MIT License. * (See accompanying file LICENSE.txt or copy at http://opensource.org/licenses/MIT) */ #pragma once #include "CoreMinimal.h" #include "Reports/GDMDebugReportRequester.h" #include "GDMRequesterTrello.generated.h" /** * Trelloへのバグレポート送信処理 */ UCLASS() class GAMEDEBUGMENU_API AGDMRequesterTrello : public AGDMDebugReportRequester { GENERATED_BODY() FString AttachmentCardListID; public: virtual void StartRequest() override; protected: virtual void RequestUploadScreenshotCapture(); virtual void RequestUploadLog(); virtual void OnResponseReceived(FHttpRequestPtr Request, FHttpResponsePtr Response, bool bWasSuccessful); virtual void OnResponseReceivedUploaded(FHttpRequestPtr Request, FHttpResponsePtr Response, bool bWasSuccessful); virtual void OnResponseReceivedLog(FHttpRequestPtr Request, FHttpResponsePtr Response, bool bWasSuccessful); }; ``` `SampleProject/Plugins/GameDebugMenu/Source/GameDebugMenu/Public/Widgets/GDMButton.h`: ```h /** * Copyright (c) 2020 akihiko moroi * * This software is released under the MIT License. * (See accompanying file LICENSE.txt or copy at http://opensource.org/licenses/MIT) */ #pragma once #include "CoreMinimal.h" #include "Components/Button.h" #include "GDMButton.generated.h" /** * GameDebugMenu用のボタン */ UCLASS() class GAMEDEBUGMENU_API UGDMButton : public UButton { GENERATED_BODY() public: UGDMButton(const FObjectInitializer& ObjectInitializer); public: #if WITH_EDITOR virtual const FText GetPaletteCategory() override; #endif protected: virtual TSharedRef RebuildWidget() override; protected: FReply GDMSlateHandleClicked(); void GDMSlateHandlePressed(); void GDMSlateHandleReleased(); void GDMSlateHandleHovered(); void GDMSlateHandleUnhovered(); }; ``` `SampleProject/Plugins/GameDebugMenu/Source/GameDebugMenu/Public/Widgets/GDMComboBoxString.h`: ```h /** * Copyright (c) 2020 akihiko moroi * * This software is released under the MIT License. * (See accompanying file LICENSE.txt or copy at http://opensource.org/licenses/MIT) */ #pragma once #include "CoreMinimal.h" #include "Components/ComboBoxString.h" #include "GDMComboBoxString.generated.h" /** * */ UCLASS() class GAMEDEBUGMENU_API UGDMComboBoxString : public UComboBoxString { GENERATED_BODY() public: UGDMComboBoxString(const FObjectInitializer& ObjectInitializer); public: #if WITH_EDITOR virtual const FText GetPaletteCategory() override; #endif }; ``` `SampleProject/Plugins/GameDebugMenu/Source/GameDebugMenu/Public/Widgets/GDMDebugReportWidget.h`: ```h /** * Copyright (c) 2020 akihiko moroi * * This software is released under the MIT License. * (See accompanying file LICENSE.txt or copy at http://opensource.org/licenses/MIT) */ #pragma once #include "CoreMinimal.h" #include "GameDebugMenuWidget.h" #include "Reports/GDMDebugReportRequester.h" #include "GDMDebugReportWidget.generated.h" DECLARE_DYNAMIC_MULTICAST_DELEGATE_OneParam(FOnReceivedReportDelegate, bool, bWasSuccessful); class UTexture2D; UCLASS() class GAMEDEBUGMENU_API UGDMDebugReportWidget : public UGameDebugMenuWidget { GENERATED_BODY() public: UPROPERTY(BlueprintAssignable) FOnReceivedReportDelegate OnReceivedReportDispatcher; UPROPERTY(BlueprintReadOnly, Transient) UTexture2D* ScreenshotTexture; UPROPERTY(BlueprintReadOnly, Transient) TArray ScreenshotImageData; UPROPERTY(BlueprintReadWrite) int32 TestCount; UPROPERTY(BlueprintReadWrite) int32 MaxTestCount; protected: FDateTime ScreenshotCapturedDateTime; TWeakObjectPtr CurrentDebugReportRequester; public: UGDMDebugReportWidget(const FObjectInitializer& ObjectInitializer); UFUNCTION(BlueprintCallable) virtual void SendDebugReport(const FString& Subject, const FString& Description,int32 IssueCategoryIndex, int32 PriorityIndex, int32 AssigneeIndex, bool bSendLogs, bool bSendScreenshotCapture); UFUNCTION(BlueprintCallable) virtual TArray GetIssueCategoryNameList(); UFUNCTION(BlueprintCallable) virtual TArray GetPriorityNameList(); UFUNCTION(BlueprintCallable) virtual TArray GetAssigneeNameList(); UFUNCTION(BlueprintPure) virtual int32 GetDefaultIssueCategoryIndex(); UFUNCTION(BlueprintPure) virtual int32 GetDefaultPriorityIndex(); virtual void OnScreenshotCaptured(int32 Width, int32 Height, const TArray& Bitmap); UFUNCTION(BlueprintImplementableEvent) void OnCreatedScreenshotTexture(UTexture2D* NewScreenshotTexture); UFUNCTION() virtual void OnRequesterDestroyed(AActor* DestroyedActor); UFUNCTION(BlueprintPure) virtual bool IsRequesting(); }; ``` `SampleProject/Plugins/GameDebugMenu/Source/GameDebugMenu/Public/Widgets/GDMFunctionWidget.h`: ```h /** * Copyright (c) 2020 akihiko moroi * * This software is released under the MIT License. * (See accompanying file LICENSE.txt or copy at http://opensource.org/licenses/MIT) */ #pragma once #include "CoreMinimal.h" #include "GameDebugMenuWidget.h" #include "GameDebugMenuTypes.h" #include "GDMFunctionWidget.generated.h" /** * */ UCLASS() class UGDMFunctionWidget : public UGameDebugMenuWidget { GENERATED_BODY() public: /* 関数所持オブジェクト */ UPROPERTY(BlueprintReadWrite, Category = "GDM|Functions") UObject* TargetObject; /* 関数名 */ UPROPERTY(BlueprintReadWrite, Category = "GDM|Functions") FName FunctionName; UPROPERTY(BlueprintReadWrite, Category = "GDM|Functions") FString FunctionSaveKey; public: UFUNCTION(BlueprintCallable) bool TryCallObjectFunction(FName EventName); protected: bool GDMProcessEvent(FName EventName, void* Parms); }; ``` `SampleProject/Plugins/GameDebugMenu/Source/GameDebugMenu/Public/Widgets/GDMIntSpinBox.h`: ```h /** * Copyright (c) 2020 akihiko moroi * * This software is released under the MIT License. * (See accompanying file LICENSE.txt or copy at http://opensource.org/licenses/MIT) */ #pragma once #include "CoreMinimal.h" #include "Components/Widget.h" #include "Fonts/SlateFontInfo.h" #include "Styling/SlateColor.h" #include "Styling/SlateTypes.h" #include "Widgets/SWidget.h" #include "Widgets/Input/SSpinBox.h" #include "GDMIntSpinBox.generated.h" /** * Int版SpinBox */ UCLASS() class UGDMIntSpinBox : public UWidget { GENERATED_UCLASS_BODY() public: DECLARE_DYNAMIC_MULTICAST_DELEGATE_OneParam(FOnSpinBoxValueChangedEvent, int32, InValue); DECLARE_DYNAMIC_MULTICAST_DELEGATE_TwoParams(FOnSpinBoxValueCommittedEvent, int32, InValue, ETextCommit::Type, CommitMethod); DECLARE_DYNAMIC_MULTICAST_DELEGATE(FOnSpinBoxBeginSliderMovement); public: /** Value stored in this spin box */ UPROPERTY(EditAnywhere, Category=Content) int32 Value; /** A bindable delegate to allow logic to drive the value of the widget */ UPROPERTY() FGetInt32 ValueDelegate; public: /** The Style */ UPROPERTY(EditAnywhere, BlueprintReadWrite, Category="Style", meta=( DisplayName="Style" )) FSpinBoxStyle WidgetStyle; UPROPERTY() USlateWidgetStyleAsset* Style_DEPRECATED; /** The amount by which to change the spin box value as the slider moves. */ UPROPERTY(EditAnywhere, BlueprintReadOnly, Category="Slider") int32 Delta; /** The exponent by which to increase the delta as the mouse moves. 1 is constant (never increases the delta). */ UPROPERTY(EditAnywhere, BlueprintReadOnly, Category="Slider") float SliderExponent; /** Font color and opacity (overrides style) */ UPROPERTY(EditAnywhere, BlueprintReadOnly, Category="Display") FSlateFontInfo Font; /** The justification the value text should appear as. */ UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "Display") TEnumAsByte Justification; /** The minimum width of the spin box */ UPROPERTY(EditAnywhere, BlueprintReadOnly, Category="Display", AdvancedDisplay, DisplayName = "Minimum Desired Width") float MinDesiredWidth; /** Whether to remove the keyboard focus from the spin box when the value is committed */ UPROPERTY(EditAnywhere, BlueprintReadOnly, Category="Input", AdvancedDisplay) bool ClearKeyboardFocusOnCommit; /** Whether to select the text in the spin box when the value is committed */ UPROPERTY(EditAnywhere, BlueprintReadOnly, Category="Input", AdvancedDisplay) bool SelectAllTextOnCommit; UPROPERTY(EditAnywhere, BlueprintReadOnly, Category="Style") FSlateColor ForegroundColor; public: /** Called when the value is changed interactively by the user */ UPROPERTY(BlueprintAssignable, Category="SpinBox|Events") FOnSpinBoxValueChangedEvent OnValueChanged; /** Called when the value is committed. Occurs when the user presses Enter or the text box loses focus. */ UPROPERTY(BlueprintAssignable, Category="SpinBox|Events") FOnSpinBoxValueCommittedEvent OnValueCommitted; /** Called right before the slider begins to move */ UPROPERTY(BlueprintAssignable, Category="SpinBox|Events") FOnSpinBoxBeginSliderMovement OnBeginSliderMovement; /** Called right after the slider handle is released by the user */ UPROPERTY(BlueprintAssignable, Category="SpinBox|Events") FOnSpinBoxValueChangedEvent OnEndSliderMovement; public: /** Get the current value of the spin box. */ UFUNCTION(BlueprintCallable, Category="Behavior") int32 GetValue() const; /** Set the value of the spin box. */ UFUNCTION(BlueprintCallable, Category="Behavior") void SetValue(int32 NewValue); public: /** Get the current minimum value that can be manually set in the spin box. */ UFUNCTION(BlueprintCallable, Category="Behavior") int32 GetMinValue() const; /** Set the minimum value that can be manually set in the spin box. */ UFUNCTION(BlueprintCallable, Category = "Behavior") void SetMinValue(int32 NewValue); /** Clear the minimum value that can be manually set in the spin box. */ UFUNCTION(BlueprintCallable, Category = "Behavior") void ClearMinValue(); /** Get the current maximum value that can be manually set in the spin box. */ UFUNCTION(BlueprintCallable, Category = "Behavior") int32 GetMaxValue() const; /** Set the maximum value that can be manually set in the spin box. */ UFUNCTION(BlueprintCallable, Category = "Behavior") void SetMaxValue(int32 NewValue); /** Clear the maximum value that can be manually set in the spin box. */ UFUNCTION(BlueprintCallable, Category = "Behavior") void ClearMaxValue(); /** Get the current minimum value that can be specified using the slider. */ UFUNCTION(BlueprintCallable, Category = "Behavior") int32 GetMinSliderValue() const; /** Set the minimum value that can be specified using the slider. */ UFUNCTION(BlueprintCallable, Category = "Behavior") void SetMinSliderValue(int32 NewValue); /** Clear the minimum value that can be specified using the slider. */ UFUNCTION(BlueprintCallable, Category = "Behavior") void ClearMinSliderValue(); /** Get the current maximum value that can be specified using the slider. */ UFUNCTION(BlueprintCallable, Category = "Behavior") int32 GetMaxSliderValue() const; /** Set the maximum value that can be specified using the slider. */ UFUNCTION(BlueprintCallable, Category = "Behavior") void SetMaxSliderValue(int32 NewValue); /** Clear the maximum value that can be specified using the slider. */ UFUNCTION(BlueprintCallable, Category = "Behavior") void ClearMaxSliderValue(); /** */ UFUNCTION(BlueprintCallable, Category = "Appearance") void SetForegroundColor(FSlateColor InForegroundColor); public: //~ Begin UWidget Interface virtual void SynchronizeProperties() override; //~ End UWidget Interface //~ Begin UVisual Interface virtual void ReleaseSlateResources(bool bReleaseChildren) override; //~ End UVisual Interface //~ Begin UObject Interface virtual void PostLoad() override; //~ End UObject Interface #if WITH_EDITOR virtual const FText GetPaletteCategory() override; #endif //~ End UWidget Interface protected: //~ Begin UWidget Interface virtual TSharedRef RebuildWidget() override; // End of UWidget void HandleOnValueChanged(int32 InValue); void HandleOnValueCommitted(int32 InValue, ETextCommit::Type CommitMethod); void HandleOnBeginSliderMovement(); void HandleOnEndSliderMovement(int32 InValue); protected: /** Whether the optional MinValue attribute of the widget is set */ UPROPERTY(EditAnywhere, Category = Content, meta=(InlineEditConditionToggle)) uint32 bOverride_MinValue : 1; /** Whether the optional MaxValue attribute of the widget is set */ UPROPERTY(EditAnywhere, Category = Content, meta=(InlineEditConditionToggle)) uint32 bOverride_MaxValue : 1; /** Whether the optional MinSliderValue attribute of the widget is set */ UPROPERTY(EditAnywhere, Category = Content, meta=(InlineEditConditionToggle)) uint32 bOverride_MinSliderValue : 1; /** Whether the optional MaxSliderValue attribute of the widget is set */ UPROPERTY(EditAnywhere, Category = Content, meta=(InlineEditConditionToggle)) uint32 bOverride_MaxSliderValue : 1; /** The minimum allowable value that can be manually entered into the spin box */ UPROPERTY(EditAnywhere, Category = Content, DisplayName = "Minimum Value", meta = (editcondition = "bOverride_MinValue")) int32 MinValue; /** The maximum allowable value that can be manually entered into the spin box */ UPROPERTY(EditAnywhere, Category = Content, DisplayName = "Maximum Value", meta = (editcondition = "bOverride_MaxValue")) int32 MaxValue; /** The minimum allowable value that can be specified using the slider */ UPROPERTY(EditAnywhere, Category = Content, DisplayName = "Minimum Slider Value", meta = (editcondition = "bOverride_MinSliderValue")) int32 MinSliderValue; /** The maximum allowable value that can be specified using the slider */ UPROPERTY(EditAnywhere, Category = Content, DisplayName = "Maximum Slider Value", meta = (editcondition = "bOverride_MaxSliderValue")) int32 MaxSliderValue; protected: TSharedPtr> MySpinBox; PROPERTY_BINDING_IMPLEMENTATION(int32, Value); }; ``` `SampleProject/Plugins/GameDebugMenu/Source/GameDebugMenu/Public/Widgets/GDMPropertyWidget.h`: ```h /** * Copyright (c) 2020 akihiko moroi * * This software is released under the MIT License. * (See accompanying file LICENSE.txt or copy at http://opensource.org/licenses/MIT) */ #pragma once #include "CoreMinimal.h" #include "GameDebugMenuWidget.h" #include "GameDebugMenuTypes.h" #include "GDMPropertyWidget.generated.h" /** * */ UCLASS() class UGDMPropertyWidget : public UGameDebugMenuWidget { GENERATED_BODY() public: /** プロパティ所持オブジェクト */ UPROPERTY(BlueprintReadWrite, Category = "GDM|Properties") TObjectPtr TargetObject; /** プロパティ名 */ UPROPERTY(BlueprintReadWrite, Category = "GDM|Properties") FName PropertyName; /** プロパティの種類 */ UPROPERTY(BlueprintReadWrite, Category = "GDM|Properties") EGDMPropertyType PropertyType; /** UIの設定情報 */ UPROPERTY(BlueprintReadWrite, Category = "GDM|Properties") FGDMPropertyUIConfigInfo PropertyConfigInfo; /** UI操作時のプロパティの変化量 */ UPROPERTY(BlueprintReadWrite, Category = "GDM|Properties") float ChangeAmount; UPROPERTY(BlueprintReadWrite, Category = "GDM|Properties") FString PropertySaveKey; protected: bool bStartChangeAmount; bool bChangedMaxChangeAmount; float ElapsedTime; float InactiveElapsedTime = 0.0f; public: virtual void NativeConstruct() override; virtual void NativeDestruct() override; virtual void NativeTick(const FGeometry& MyGeometry, float InDeltaTime) override; public: UFUNCTION(BlueprintCallable, Category = "GDM") virtual void StartChangeAmountTime(); UFUNCTION(BlueprintCallable, Category = "GDM") virtual void ResetChangeAmountTime(); UFUNCTION(BlueprintCallable, Category = "GDM|Properties") bool GetPropertyValue_Bool(bool& bHasProperty); UFUNCTION(BlueprintCallable, Category = "GDM|Properties") void SetPropertyValue_Bool(bool bNewValue, bool& bHasProperty); UFUNCTION(BlueprintCallable, Category = "GDM|Properties") float GetPropertyValue_Float(bool& bHasProperty); UFUNCTION(BlueprintCallable, Category = "GDM|Properties") void SetPropertyValue_Float(float NewValue, bool& bHasProperty); UFUNCTION(BlueprintCallable, Category = "GDM|Properties") int32 GetPropertyValue_Int(bool& bHasProperty); UFUNCTION(BlueprintCallable, Category = "GDM|Properties") void SetPropertyValue_Int(int32 NewValue, bool& bHasProperty); UFUNCTION(BlueprintCallable, Category = "GDM|Properties") uint8 GetPropertyValue_Byte(bool& bHasProperty); UFUNCTION(BlueprintCallable, Category = "GDM|Properties") void SetPropertyValue_Byte(uint8 NewValue, bool& bHasProperty); UFUNCTION(BlueprintCallable, Category = "GDM|Properties") TArray GetEnumDisplayNames(const FString& EnumPath, bool& bHasProperty); UFUNCTION(BlueprintCallable, Category = "GDM|Properties") FString GetPropertyValue_String(bool& bHasProperty); UFUNCTION(BlueprintCallable, Category = "GDM|Properties") void SetPropertyValue_String(FString NewValue, bool& bHasProperty); UFUNCTION(BlueprintCallable, Category = "GDM|Properties") FVector GetPropertyValue_Vector(bool& bHasProperty); UFUNCTION(BlueprintCallable, Category = "GDM|Properties") void SetPropertyValue_Vector(FVector NewValue, bool& bHasProperty); UFUNCTION(BlueprintCallable, Category = "GDM|Properties") FVector2D GetPropertyValue_Vector2D(bool& bHasProperty); UFUNCTION(BlueprintCallable, Category = "GDM|Properties") void SetPropertyValue_Vector2D(FVector2D NewValue, bool& bHasProperty); UFUNCTION(BlueprintCallable, Category = "GDM|Properties") FRotator GetPropertyValue_Rotator(bool& bHasProperty); UFUNCTION(BlueprintCallable, Category = "GDM|Properties") void SetPropertyValue_Rotator(FRotator NewValue, bool& bHasProperty); }; ``` `SampleProject/Plugins/GameDebugMenu/Source/GameDebugMenu/Public/Widgets/GDMTextBlock.h`: ```h /** * Copyright (c) 2020 akihiko moroi * * This software is released under the MIT License. * (See accompanying file LICENSE.txt or copy at http://opensource.org/licenses/MIT) */ #pragma once #include "CoreMinimal.h" #include "Components/TextBlock.h" #include "GDMTextBlock.generated.h" /** * */ UCLASS() class GAMEDEBUGMENU_API UGDMTextBlock : public UTextBlock { GENERATED_BODY() public: UPROPERTY(BlueprintReadOnly,EditAnywhere, Category = "GDM|Config") FString DebugMenuStringKey; #if WITH_EDITORONLY_DATA UPROPERTY(EditAnywhere, Category = "GDM|Config|Editor") FName PreviewLanguageKey; #endif public: UGDMTextBlock(const FObjectInitializer& ObjectInitializer); public: virtual void SynchronizeProperties() override; virtual void SetText(FText InText) override; UFUNCTION(BlueprintCallable, Category = "Appearance") virtual void SetWrapTextAt(float InWrapTextAt); public: #if WITH_EDITOR virtual const FText GetPaletteCategory() override; virtual bool CanEditChange(const FProperty* InProperty) const override; #endif }; ``` `SampleProject/Plugins/GameDebugMenu/Source/GameDebugMenu/Public/Widgets/GameDebugMenuRootWidget.h`: ```h /** * Copyright (c) 2020 akihiko moroi * * This software is released under the MIT License. * (See accompanying file LICENSE.txt or copy at http://opensource.org/licenses/MIT) */ #pragma once #include "CoreMinimal.h" #include "GameDebugMenuWidget.h" #include "GameDebugMenuRootWidget.generated.h" class AGameDebugMenuManager; /** * DebugMenuのルートに当たるWidget */ UCLASS() class GAMEDEBUGMENU_API UGameDebugMenuRootWidget : public UGameDebugMenuWidget { GENERATED_BODY() UPROPERTY(Transient) TObjectPtr Manager = nullptr; public: virtual void NativeConstruct() override; virtual void NativeDestruct() override; virtual void ActivateDebugMenu() override; virtual void DeactivateDebugMenu() override; public: void SetDebugMenuManager(AGameDebugMenuManager* InManager); UFUNCTION(BlueprintImplementableEvent) void InitializeRootWidget(); UFUNCTION(BlueprintImplementableEvent) void ShowDebugReport(); UFUNCTION(BlueprintPure) AGameDebugMenuManager* GetOwnerManager() const; UFUNCTION(BlueprintCallable) void SwitchInputComponentGroupForGameDebugMenu(FName NewGroupName); }; ``` `SampleProject/Plugins/GameDebugMenu/Source/GameDebugMenu/Public/Widgets/GameDebugMenuWidget.h`: ```h /** * Copyright (c) 2020 akihiko moroi * * This software is released under the MIT License. * (See accompanying file LICENSE.txt or copy at http://opensource.org/licenses/MIT) */ #pragma once #include "CoreMinimal.h" #include "Blueprint/UserWidget.h" #include "GameDebugMenuTypes.h" #include "GameDebugMenuWidget.generated.h" class UInputAction; class UGDMEnhancedInputComponent; DECLARE_DYNAMIC_MULTICAST_DELEGATE_TwoParams(FGDMWidgetDelegate, UGameDebugMenuWidget*, TargetWidget, FName, EventName); DECLARE_DYNAMIC_DELEGATE( FOnGameDebugMenuWidgetInputAction ); /** * DebugMenu用Widgetの基底クラス * ページを増やす場合はこれを継承させて作成 */ UCLASS(Abstract, Blueprintable, BlueprintType) class GAMEDEBUGMENU_API UGameDebugMenuWidget : public UUserWidget { GENERATED_BODY() public: /** イベント通知ディスパッチャー */ UPROPERTY(BlueprintAssignable) FGDMWidgetDelegate OnSendWidgetEventDispatcher; protected: bool bActivateMenu; TArray InputHandles; public: UGameDebugMenuWidget(const FObjectInitializer& ObjectInitializer); protected: virtual void CreateInputComponent() override; virtual void StartProcessingInputScriptDelegates() override; virtual void StopProcessingInputScriptDelegates() override; public: /** * UE5.7+: UUserWidget::InitializeInputComponent を override できなくなったため、 * DebugMenu用InputComponent生成は自前で行う(登録/Pushは行わない)。 */ UFUNCTION(BlueprintCallable, Category = "GDM|Input") void EnsureDebugMenuInputComponent(); /** * ローカルのコントローラーを取得(DebugCamera操作中でも取得できるもの) */ UFUNCTION(BlueprintPure, Category = "GDM") APlayerController* GetOriginalPlayerController() const; /** * Widgetが所持するInputComponentを返す */ UFUNCTION(BlueprintPure, Category = "GDM|Input") UGDMEnhancedInputComponent* GetMyInputComponent() const; /** * 指定のInputActionが入力されたときに呼び出される関数名を登録する * @param Action - 対象となる入力アクション * @param FunctionName - 呼び出される関数名 * @param TriggerEvent - 入力イベント * @param FunctionObject - 関数名を持つオブジェクト。nullなら自分自身 */ UFUNCTION(BlueprintCallable, Category = "GDM|Input", meta = (AdvancedDisplay = "2")) bool RegisterDebugMenuWidgetInputFunction(const UInputAction* Action, const FName FunctionName, const ETriggerEvent TriggerEvent = ETriggerEvent::Triggered, UObject* FunctionObject = nullptr); /** * 指定のInputActionが入力されたときに呼び出されるイベントを登録する * @param Action - 対象となる入力アクション * @param Callback - 入力時に呼び出されるイベント * @param TriggerEvent - 入力イベント */ UFUNCTION(BlueprintCallable, Category = "GDM|Input", meta = (AdvancedDisplay = "2")) bool RegisterDebugMenuWidgetInputEvent(const UInputAction* Action, FOnGameDebugMenuWidgetInputAction Callback, const ETriggerEvent TriggerEvent = ETriggerEvent::Triggered); /** * 登録した入力イベントを解除する */ UFUNCTION(BlueprintCallable, Category = "GDM|Input") void UnregisterDebugMenuWidgetInputs(); /** * イベント通知 */ UFUNCTION(BlueprintCallable, Category = "GDM|Event") void SendSelfEvent(FName EventName); /** * コンソールコマンドを実行する */ UFUNCTION(BlueprintCallable, Category = "GDM|Command") void ExecuteGDMConsoleCommand(const FString Command, const EGDMConsoleCommandNetType CommandNetType); /** * UIを表示するときのイベント * @param bRequestDebugMenuManager - マネージャーから呼び出された場合trueになる。 * @note 呼ばれるのはActivateされてるときのみ */ UFUNCTION(BlueprintCallable, BlueprintImplementableEvent, Category = "GDM|Event", meta = (AdvancedDisplay ="bRequestDebugMenuManager")) void OnShowingMenu(bool bRequestDebugMenuManager); /** * UIを非表示にするときのイベント * @param bRequestDebugMenuManager - マネージャーから呼び出された場合trueになる。 * @note 呼ばれるのはActivateされてるときのみ */ UFUNCTION(BlueprintCallable, BlueprintImplementableEvent, Category = "GDM|Event", meta = (AdvancedDisplay ="bRequestDebugMenuManager")) void OnHidingMenu(bool bRequestDebugMenuManager); /** * アクティベート化をするときに呼ばれるイベント * @note ここで表示したり、操作できる状態に移行する */ UFUNCTION(BlueprintImplementableEvent, Category = "GDM|Event") void OnActivateDebugMenu(); /** * ディアクティベート化をするときに呼ばれるイベント * @note ここで非表示にしたり、メニューの終了処理を行う */ UFUNCTION(BlueprintImplementableEvent, Category = "GDM|Event") void OnDeactivateDebugMenu(); /** * 画面を最新の状態し直したいときに呼ばれるイベント * @note UI上で参照するデータを取得し直すなどをここでする */ UFUNCTION(BlueprintCallable, BlueprintImplementableEvent, Category = "GDM|Event") void OnRefreshDataAndDisplay(); /** * アクティベート状態であればTrue */ UFUNCTION(BlueprintPure, Category = "GDM|Event") virtual bool IsActivateDebugMenu(); /** * UIのアクティベート化をする(表示、操作できる状態になる) */ UFUNCTION(BlueprintCallable, Category = "GDM|Event") virtual void ActivateDebugMenu(); /** * UIのディアクティベート化をする(非表示、操作不可状態になる) */ UFUNCTION(BlueprintCallable, Category = "GDM|Event") virtual void DeactivateDebugMenu(); UFUNCTION() virtual void OnChangeDebugMenuLanguage(const FName& NewLanguageKey, const FName& OldLanguageKey); /** * デバックメニューの使用言語が変更されたら呼ばれる */ UFUNCTION(BlueprintImplementableEvent, Category = "GDM|Language") void OnChangeDebugMenuLanguageBP(const FName& NewLanguageKey, const FName& OldLanguageKey); /** * 指定クラスの子供Widgetをすべて取得する * @param WidgetClass - 対象のクラス * @param OutChildWidgets - 取得できたWidget * @param bEndSearchAsYouFind - 最初に見つけた時点で終了する * @return true: 1つ以上OutChildWidgetsがある false: 1つもWidgetがなかった */ UFUNCTION(BlueprintCallable, Category = "GDM", meta = (DeterminesOutputType = "WidgetClass", DynamicOutputParam = "OutChildWidgets")) virtual bool GetWidgetChildrenOfClass(TSubclassOf WidgetClass, TArray& OutChildWidgets, bool bEndSearchAsYouFind); }; ``` `SampleProject/Plugins/GameDebugMenu/Source/GameDebugMenuEditor/GameDebugMenuEditor.Build.cs`: ```cs /** * Copyright (c) 2020 akihiko moroi * * This software is released under the MIT License. * (See accompanying file LICENSE.txt or copy at http://opensource.org/licenses/MIT) */ using UnrealBuildTool; public class GameDebugMenuEditor : ModuleRules { public GameDebugMenuEditor(ReadOnlyTargetRules Target) : base(Target) { PrivateIncludePaths.AddRange( new string[] { "GameDebugMenuEditor/Private", "GameDebugMenuEditor/Private/AssetTypeActions", "GameDebugMenuEditor/Private/DetailCustomizations", "GameDebugMenuEditor/Private/Factory", "GameDebugMenuEditor/Private/Pins", } ); PublicDependencyModuleNames.AddRange( new string[] { "Core", "UMG", "UnrealEd", "UMGEditor", "GameDebugMenu", } ); PrivateDependencyModuleNames.AddRange( new string[] { "CoreUObject", "Engine", "Slate", "SlateCore", "GraphEditor", "BlueprintGraph", } ); } } ``` `SampleProject/Plugins/GameDebugMenu/Source/GameDebugMenuEditor/Private/AssetTypeActions/AssetTypeActions_GDMPlayerControllerProxyComponent.cpp`: ```cpp /** * Copyright (c) 2020 akihiko moroi * * This software is released under the MIT License. * (See accompanying file LICENSE.txt or copy at http://opensource.org/licenses/MIT) */ #include "AssetTypeActions/AssetTypeActions_GDMPlayerControllerProxyComponent.h" #include "GameDebugMenuEditor.h" #include "Component/GDMPlayerControllerProxyComponent.h" FText FAssetTypeActions_GDMPlayerControllerProxyComponent::GetName() const { return NSLOCTEXT("AssetTypeActions", "AssetTypeActions_GDMPlayerControllerProxyComponent", "GDM Player Controller Proxy Component"); } FColor FAssetTypeActions_GDMPlayerControllerProxyComponent::GetTypeColor() const { return FColor(60, 60, 60); } UClass* FAssetTypeActions_GDMPlayerControllerProxyComponent::GetSupportedClass() const { return UGDMPlayerControllerProxyComponent::StaticClass(); } uint32 FAssetTypeActions_GDMPlayerControllerProxyComponent::GetCategories() { return FGameDebugMenuEditorModule::GetAssetCategory(); } bool FAssetTypeActions_GDMPlayerControllerProxyComponent::CanLocalize() const { return false; } FText FAssetTypeActions_GDMPlayerControllerProxyComponent::GetAssetDescription(const FAssetData& AssetData) const { return FText::FromString(FString(TEXT("GDM Player Controller Proxy Component"))); } ``` `SampleProject/Plugins/GameDebugMenu/Source/GameDebugMenuEditor/Private/AssetTypeActions/AssetTypeActions_GameDebugMenuManager.cpp`: ```cpp /** * Copyright (c) 2020 akihiko moroi * * This software is released under the MIT License. * (See accompanying file LICENSE.txt or copy at http://opensource.org/licenses/MIT) */ #include "AssetTypeActions/AssetTypeActions_GameDebugMenuManager.h" #include "GameDebugMenuManager.h" #include "GameDebugMenuEditor.h" FText FAssetTypeActions_GameDebugMenuManager::GetName() const { return NSLOCTEXT("AssetTypeActions", "AssetTypeActions_GameDebugMenuManager", "Game Debug Menu Manager"); } FColor FAssetTypeActions_GameDebugMenuManager::GetTypeColor() const { return FColor(60, 60, 60); } UClass* FAssetTypeActions_GameDebugMenuManager::GetSupportedClass() const { return AGameDebugMenuManager::StaticClass(); } uint32 FAssetTypeActions_GameDebugMenuManager::GetCategories() { return FGameDebugMenuEditorModule::GetAssetCategory(); } bool FAssetTypeActions_GameDebugMenuManager::CanLocalize() const { return false; } FText FAssetTypeActions_GameDebugMenuManager::GetAssetDescription(const FAssetData& AssetData) const { return FText::FromString(FString(TEXT("Game Debug Menu Manager"))); } ``` `SampleProject/Plugins/GameDebugMenu/Source/GameDebugMenuEditor/Private/AssetTypeActions/AssetTypeActions_GameDebugMenuWidget.cpp`: ```cpp /** * Copyright (c) 2020 akihiko moroi * * This software is released under the MIT License. * (See accompanying file LICENSE.txt or copy at http://opensource.org/licenses/MIT) */ #include "AssetTypeActions/AssetTypeActions_GameDebugMenuWidget.h" #include "GameDebugMenuEditor.h" #include "Widgets/GameDebugMenuWidget.h" FText FAssetTypeActions_GameDebugMenuWidget::GetName() const { return NSLOCTEXT("AssetTypeActions", "AssetTypeActions_GameDebugMenuWidget", "Game Debug Menu Widget"); } FColor FAssetTypeActions_GameDebugMenuWidget::GetTypeColor() const { return FColor(60, 60, 60); } UClass* FAssetTypeActions_GameDebugMenuWidget::GetSupportedClass() const { return UGameDebugMenuWidget::StaticClass(); } uint32 FAssetTypeActions_GameDebugMenuWidget::GetCategories() { return FGameDebugMenuEditorModule::GetAssetCategory(); } bool FAssetTypeActions_GameDebugMenuWidget::CanLocalize() const { return false; } FText FAssetTypeActions_GameDebugMenuWidget::GetAssetDescription(const FAssetData& AssetData) const { return FText::FromString(FString(TEXT("Game Debug Menu Widget"))); } ``` `SampleProject/Plugins/GameDebugMenu/Source/GameDebugMenuEditor/Private/DetailCustomizations/GDMGameplayCategoryKeyCustomization.cpp`: ```cpp /** * Copyright (c) 2020 akihiko moroi * * This software is released under the MIT License. * (See accompanying file LICENSE.txt or copy at http://opensource.org/licenses/MIT) */ #include "GDMGameplayCategoryKeyCustomization.h" #include "PropertyHandle.h" #include "DetailWidgetRow.h" #include "Widgets/DeclarativeSyntaxSupport.h" #include "Widgets/Input/STextComboBox.h" #include "GameDebugMenuSettings.h" #define LOCTEXT_NAMESPACE "GDMGameplayCategoryKeyCustomization" /************************************************************************/ /* FGOAPStateKeyCustomization */ /************************************************************************/ TSharedRef FGDMGameplayCategoryKeyCustomization::MakeInstance() { return MakeShareable(new FGDMGameplayCategoryKeyCustomization()); } void FGDMGameplayCategoryKeyCustomization::CustomizeHeader(TSharedRef PropertyHandle, FDetailWidgetRow& HeaderRow, IPropertyTypeCustomizationUtils& CustomizationUtils) { MakeGameplayCategoryNames(); uint32 NumChildren; PropertyHandle->GetNumChildren(NumChildren); for( uint32 ChildIndex = 0; ChildIndex < NumChildren; ++ChildIndex ) { const TSharedPtr ChildHandle = PropertyHandle->GetChildHandle(ChildIndex); if( ChildHandle->GetProperty()->GetName() == TEXT("Index") ) { IndexHandle = ChildHandle; int32 CategoryIndex = 0; ChildHandle->GetValue(CategoryIndex); ArrayIndex = GetArrayIndex(CategoryIndex); break; } } if( ArrayIndex >= GameplayCategoryNames.Num() ) { ArrayIndex = 0; } check(IndexHandle.IsValid()); HeaderRow .NameContent() [ PropertyHandle->CreatePropertyNameWidget() ] .ValueContent() .MinDesiredWidth(500) [ SNew(SHorizontalBox) + SHorizontalBox::Slot() .HAlign(HAlign_Left) [ SAssignNew(TextComboBox, STextComboBox) .OptionsSource(&GameplayCategoryNames) .OnSelectionChanged(this, &FGDMGameplayCategoryKeyCustomization::OnSelectionChanged) .InitiallySelectedItem(GameplayCategoryNames[ArrayIndex]) ] ]; } void FGDMGameplayCategoryKeyCustomization::MakeGameplayCategoryNames() { GameplayCategoryNames.Reset(); UGameDebugMenuSettings* Settings = GetMutableDefault(); for( int32 Index = 0; Index < Settings->OrderGameplayCategoryTitles.Num(); ++Index ) { GameplayCategoryNames.Add(MakeShareable(new FString(Settings->GetGameplayCategoryTitle(Index)))); } } void FGDMGameplayCategoryKeyCustomization::OnSelectionChanged(TSharedPtr ItemSelected, ESelectInfo::Type SelectInfo) { if( ItemSelected.IsValid() ) { for( int32 Index = 0; Index < GameplayCategoryNames.Num(); ++Index ) { if( GameplayCategoryNames[Index] == ItemSelected ) { ArrayIndex = Index; break; } } UGameDebugMenuSettings* Settings = GetMutableDefault(); IndexHandle->SetValue(Settings->GetGameplayCategoryIndex(ArrayIndex)); } } int32 FGDMGameplayCategoryKeyCustomization::GetArrayIndex(int32 CategoryIndex) { int32 ReturnValue = INDEX_NONE; UGameDebugMenuSettings* Settings = GetMutableDefault(); for( int32 Idx = 0; Idx < Settings->OrderGameplayCategoryTitles.Num(); ++Idx ) { if( Settings->OrderGameplayCategoryTitles[Idx].Index == CategoryIndex ) { ReturnValue = Idx; break; } } if( ReturnValue == INDEX_NONE ) { ReturnValue = 0; } return ReturnValue; } #undef LOCTEXT_NAMESPACE ``` `SampleProject/Plugins/GameDebugMenu/Source/GameDebugMenuEditor/Private/DetailCustomizations/GDMGameplayCategoryKeyCustomization.h`: ```h /** * Copyright (c) 2020 akihiko moroi * * This software is released under the MIT License. * (See accompanying file LICENSE.txt or copy at http://opensource.org/licenses/MIT) */ #pragma once #include "IPropertyTypeCustomization.h" #include class FGDMGameplayCategoryKeyCustomization : public IPropertyTypeCustomization { protected: TArray> GameplayCategoryNames; TSharedPtr IndexHandle; TSharedPtr TextComboBox; int32 ArrayIndex = 0; public: static TSharedRef MakeInstance(); /* Begin IPropertyTypeCustomization */ virtual void CustomizeHeader(TSharedRef PropertyHandle, FDetailWidgetRow& HeaderRow, IPropertyTypeCustomizationUtils& CustomizationUtils) override; virtual void CustomizeChildren(TSharedRef PropertyHandle, IDetailChildrenBuilder& ChildBuilder, IPropertyTypeCustomizationUtils& CustomizationUtils) override {}; /* End IPropertyTypeCustomization */ private: void MakeGameplayCategoryNames(); void OnSelectionChanged(TSharedPtr ItemSelected, ESelectInfo::Type SelectInfo); int32 GetArrayIndex(int32 CategoryIndex); }; ``` `SampleProject/Plugins/GameDebugMenu/Source/GameDebugMenuEditor/Private/Factory/GDMPlayerControllerProxyComponentFactory.cpp`: ```cpp /** * Copyright (c) 2020 akihiko moroi * * This software is released under the MIT License. * (See accompanying file LICENSE.txt or copy at http://opensource.org/licenses/MIT) */ #include "Factory/GDMPlayerControllerProxyComponentFactory.h" #include "Component/GDMPlayerControllerProxyComponent.h" #include "Kismet2/KismetEditorUtilities.h" #define LOCTEXT_NAMESPACE "GameDebugMenu" UGDMPlayerControllerProxyComponentFactory::UGDMPlayerControllerProxyComponentFactory(const class FObjectInitializer& Object) : Super(Object) { SupportedClass = UGDMPlayerControllerProxyComponent::StaticClass(); bEditAfterNew = true; bCreateNew = true; } UObject* UGDMPlayerControllerProxyComponentFactory::FactoryCreateNew(UClass* Class, UObject* InParent, FName Name, EObjectFlags Flags, UObject* Context, FFeedbackContext* Warn) { check(Class->IsChildOf(UGDMPlayerControllerProxyComponent::StaticClass())); UBlueprint* Blueprint = FKismetEditorUtilities::CreateBlueprint(Class, InParent, Name, BPTYPE_Normal, UBlueprint::StaticClass(), UBlueprintGeneratedClass::StaticClass(), TEXT("AssetTypeActions")); return Blueprint; } #undef LOCTEXT_NAMESPACE ``` `SampleProject/Plugins/GameDebugMenu/Source/GameDebugMenuEditor/Private/Factory/GameDebugMenuManagerFactory.cpp`: ```cpp /** * Copyright (c) 2020 akihiko moroi * * This software is released under the MIT License. * (See accompanying file LICENSE.txt or copy at http://opensource.org/licenses/MIT) */ #include "Factory/GameDebugMenuManagerFactory.h" #include "Kismet2/KismetEditorUtilities.h" #include "GameDebugMenuManager.h" #define LOCTEXT_NAMESPACE "GameDebugMenu" UGameDebugMenuManagerFactory::UGameDebugMenuManagerFactory(const class FObjectInitializer& Object) : Super(Object) { SupportedClass = AGameDebugMenuManager::StaticClass(); bEditAfterNew = true; bCreateNew = true; } UObject* UGameDebugMenuManagerFactory::FactoryCreateNew(UClass* Class, UObject* InParent, FName Name, EObjectFlags Flags, UObject* Context, FFeedbackContext* Warn) { check(Class->IsChildOf(AGameDebugMenuManager::StaticClass())); return FKismetEditorUtilities::CreateBlueprint(Class, InParent, Name, BPTYPE_Normal, UBlueprint::StaticClass(), UBlueprintGeneratedClass::StaticClass(), TEXT("AssetTypeActions")); } #undef LOCTEXT_NAMESPACE ``` `SampleProject/Plugins/GameDebugMenu/Source/GameDebugMenuEditor/Private/Factory/GameDebugMenuWidgetFactory.cpp`: ```cpp /** * Copyright (c) 2020 akihiko moroi * * This software is released under the MIT License. * (See accompanying file LICENSE.txt or copy at http://opensource.org/licenses/MIT) */ #include "Factory/GameDebugMenuWidgetFactory.h" #include "WidgetBlueprint.h" #include "Widgets/GameDebugMenuWidget.h" #include "Kismet2/KismetEditorUtilities.h" #define LOCTEXT_NAMESPACE "GameDebugMenu" UGameDebugMenuWidgetFactory::UGameDebugMenuWidgetFactory(const class FObjectInitializer& Object) : Super(Object) { SupportedClass = UGameDebugMenuWidget::StaticClass(); bEditAfterNew = true; bCreateNew = true; } UObject* UGameDebugMenuWidgetFactory::FactoryCreateNew(UClass* Class, UObject* InParent, FName Name, EObjectFlags Flags, UObject* Context, FFeedbackContext* Warn) { check(Class->IsChildOf(UGameDebugMenuWidget::StaticClass())); UBlueprint* Blueprint = FKismetEditorUtilities::CreateBlueprint(Class, InParent, Name, BPTYPE_Normal, UWidgetBlueprint::StaticClass(), UBlueprintGeneratedClass::StaticClass(), TEXT("AssetTypeActions")); return Blueprint; } #undef LOCTEXT_NAMESPACE ``` `SampleProject/Plugins/GameDebugMenu/Source/GameDebugMenuEditor/Private/GameDebugMenuEditor.cpp`: ```cpp /** * Copyright (c) 2020 akihiko moroi * * This software is released under the MIT License. * (See accompanying file LICENSE.txt or copy at http://opensource.org/licenses/MIT) */ #include "GameDebugMenuEditor.h" #include "ISettingsModule.h" #include "GameDebugMenuSettings.h" #include #include "AssetTypeActions/AssetTypeActions_GameDebugMenuManager.h" #include "AssetTypeActions/AssetTypeActions_GDMPlayerControllerProxyComponent.h" #include "AssetTypeActions/AssetTypeActions_GameDebugMenuWidget.h" #include #include #include "GDMGameplayCategoryKeyCustomization.h" #include "Pins/GDMGameplayCategoryKeyPinFactory.h" #define LOCTEXT_NAMESPACE "FGameDebugMenuEditorModule" EAssetTypeCategories::Type FGameDebugMenuEditorModule::GDMAssetCategory; void FGameDebugMenuEditorModule::StartupModule() { IAssetTools& AssetTools = FModuleManager::LoadModuleChecked("AssetTools").Get(); GDMAssetCategory = AssetTools.RegisterAdvancedAssetCategory(FName(TEXT("GameDebugMenu")), LOCTEXT("GameDebugMenuAssetsCategory", "Game Debug Menu")); RegisterAssetTypeAction(AssetTools, MakeShareable(new FAssetTypeActions_GameDebugMenuManager())); RegisterAssetTypeAction(AssetTools, MakeShareable(new FAssetTypeActions_GDMPlayerControllerProxyComponent())); RegisterAssetTypeAction(AssetTools, MakeShareable(new FAssetTypeActions_GameDebugMenuWidget())); FPropertyEditorModule& PropertyModule = FModuleManager::LoadModuleChecked("PropertyEditor"); PropertyModule.RegisterCustomPropertyTypeLayout("GDMGameplayCategoryKey", FOnGetPropertyTypeCustomizationInstance::CreateStatic(&FGDMGameplayCategoryKeyCustomization::MakeInstance)); GameplayCategoryKeyPinFactory = MakeShareable(new FGDMGameplayCategoryKeyPinFactory()); FEdGraphUtilities::RegisterVisualPinFactory(GameplayCategoryKeyPinFactory); } void FGameDebugMenuEditorModule::ShutdownModule() { if (FModuleManager::Get().IsModuleLoaded("AssetTools")) { IAssetTools& AssetTools = FModuleManager::GetModuleChecked("AssetTools").Get(); for (int32 Index = 0; Index < CreatedAssetTypeActions.Num(); ++Index) { AssetTools.UnregisterAssetTypeActions(CreatedAssetTypeActions[Index].ToSharedRef()); } } CreatedAssetTypeActions.Empty(); FPropertyEditorModule& PropertyModule = FModuleManager::LoadModuleChecked("PropertyEditor"); PropertyModule.UnregisterCustomPropertyTypeLayout("GDMGameplayCategoryKey"); FEdGraphUtilities::UnregisterVisualPinFactory(GameplayCategoryKeyPinFactory); GameplayCategoryKeyPinFactory = nullptr; } EAssetTypeCategories::Type FGameDebugMenuEditorModule::GetAssetCategory() { return GDMAssetCategory; } void FGameDebugMenuEditorModule::RegisterAssetTypeAction(IAssetTools& AssetTools, TSharedRef Action) { AssetTools.RegisterAssetTypeActions(Action); CreatedAssetTypeActions.Add(Action); } #undef LOCTEXT_NAMESPACE IMPLEMENT_MODULE(FGameDebugMenuEditorModule, GameDebugMenuEditor) ``` `SampleProject/Plugins/GameDebugMenu/Source/GameDebugMenuEditor/Private/Pins/GDMGameplayCategoryKeyPin.cpp`: ```cpp /** * Copyright (c) 2020 akihiko moroi * * This software is released under the MIT License. * (See accompanying file LICENSE.txt or copy at http://opensource.org/licenses/MIT) */ #include "GDMGameplayCategoryKeyPin.h" #include "EdGraph/EdGraphPin.h" #include "EdGraph/EdGraphSchema.h" #include "Widgets/Input/STextComboBox.h" #include "GameDebugMenuSettings.h" #include "GameDebugMenuTypes.h" /************************************************************************/ /* SGoapStateKeyPin */ /************************************************************************/ void SGDMGameplayCategoryKeyPin::Construct(const FArguments& InArgs, UEdGraphPin* InGraphPinObj) { SGraphPin::Construct(SGraphPin::FArguments(), InGraphPinObj); } TSharedRef SGDMGameplayCategoryKeyPin::GetDefaultValueWidget() { MakeGameplayCategoryNames(); ArrayIndex = GetCategoryNameArrayIndex(); TextComboBox = SNew(STextComboBox) .Visibility(this, &SGDMGameplayCategoryKeyPin::GetStateKeyVisibility) .OptionsSource(&GameplayCategoryNames) .OnSelectionChanged(this, &SGDMGameplayCategoryKeyPin::OnSelectionChanged) .InitiallySelectedItem(GameplayCategoryNames[ArrayIndex]); return SNew(SHorizontalBox) + SHorizontalBox::Slot() .HAlign(HAlign_Left) [ TextComboBox.ToSharedRef() ]; } void SGDMGameplayCategoryKeyPin::MakeGameplayCategoryNames() { GameplayCategoryNames.Empty(); UGameDebugMenuSettings* Settings = GetMutableDefault(); for( int32 Index = 0; Index < Settings->OrderGameplayCategoryTitles.Num(); ++Index ) { GameplayCategoryNames.Add(MakeShareable(new FString(Settings->GetGameplayCategoryTitle(Index)))); } } int32 SGDMGameplayCategoryKeyPin::GetCategoryNameArrayIndex() const { int32 GraphPinValue = 0; const FString CurrentDefault = GraphPinObj->GetDefaultAsString(); if( CurrentDefault.Len() > 0 ) { constexpr int32 StartIndex = 7;/* (Index= */ int32 EndIndex; CurrentDefault.FindChar(',', EndIndex); const FString DefaultValString = CurrentDefault.Mid(StartIndex, EndIndex - StartIndex); GraphPinValue = FCString::Atoi(*DefaultValString); } int32 Index = 0; UGameDebugMenuSettings* Settings = GetMutableDefault(); for( const FGDMOrderMenuCategoryTitle& CategoryTitle : Settings->OrderGameplayCategoryTitles ) { if( CategoryTitle.Index == GraphPinValue ) { return Index; } Index++; } return 0; } void SGDMGameplayCategoryKeyPin::SetCategoryValue(const int32 InArrayIndex) const { UGameDebugMenuSettings* Settings = GetMutableDefault(); FString StrKey; StrKey.AppendInt(Settings->GetGameplayCategoryIndex(InArrayIndex)); const FString NewValue = TEXT("(Index=") + StrKey + TEXT(",KeyName=)"); GraphPinObj->GetSchema()->TrySetDefaultValue(*GraphPinObj, NewValue); } void SGDMGameplayCategoryKeyPin::OnSelectionChanged(TSharedPtr ItemSelected, ESelectInfo::Type SelectInfo) { if( ItemSelected.IsValid() ) { for( int32 Index = 0; Index < GameplayCategoryNames.Num(); ++Index ) { if( GameplayCategoryNames[Index] == ItemSelected ) { ArrayIndex = Index; break; } } } SetCategoryValue(ArrayIndex); } EVisibility SGDMGameplayCategoryKeyPin::GetStateKeyVisibility() const { return IsConnected() ? EVisibility::Collapsed : EVisibility::Visible; } ``` `SampleProject/Plugins/GameDebugMenu/Source/GameDebugMenuEditor/Private/Pins/GDMGameplayCategoryKeyPin.h`: ```h /** * Copyright (c) 2020 akihiko moroi * * This software is released under the MIT License. * (See accompanying file LICENSE.txt or copy at http://opensource.org/licenses/MIT) */ #pragma once #include "SlateBasics.h" #include "SGraphPin.h" /************************************************************************ /* SGDMGameplayCategoryKeyPin */ /************************************************************************/ class SGDMGameplayCategoryKeyPin : public SGraphPin { int32 ArrayIndex = 0; TArray> GameplayCategoryNames; TSharedPtr TextComboBox; public: SLATE_BEGIN_ARGS(SGDMGameplayCategoryKeyPin) {} SLATE_END_ARGS() public: void Construct(const FArguments& InArgs, UEdGraphPin* InGraphPinObj); virtual TSharedRef GetDefaultValueWidget() override; private: void MakeGameplayCategoryNames(); int32 GetCategoryNameArrayIndex() const; void SetCategoryValue(const int32 InArrayIndex) const; void OnSelectionChanged(TSharedPtr ItemSelected, ESelectInfo::Type SelectInfo); EVisibility GetStateKeyVisibility() const; }; ``` `SampleProject/Plugins/GameDebugMenu/Source/GameDebugMenuEditor/Private/Pins/GDMGameplayCategoryKeyPinFactory.h`: ```h /** * Copyright (c) 2020 akihiko moroi * * This software is released under the MIT License. * (See accompanying file LICENSE.txt or copy at http://opensource.org/licenses/MIT) */ #pragma once #include "SlateBasics.h" #include "EdGraph/EdGraphPin.h" #include "EdGraphSchema_K2.h" #include "EdGraphUtilities.h" #include "GDMGameplayCategoryKeyPin.h" class FGDMGameplayCategoryKeyPinFactory : public FGraphPanelPinFactory { /* 登録したオブジェクトなら自作ピンを返す */ virtual TSharedPtr CreatePin(class UEdGraphPin* InPin) const override { const UEdGraphSchema_K2* K2Schema = GetDefault(); if (InPin->PinType.PinSubCategoryObject == FGDMGameplayCategoryKey::StaticStruct()) { return SNew(SGDMGameplayCategoryKeyPin, InPin); } return nullptr; } }; ``` `SampleProject/Plugins/GameDebugMenu/Source/GameDebugMenuEditor/Public/AssetTypeActions/AssetTypeActions_GDMPlayerControllerProxyComponent.h`: ```h /** * Copyright (c) 2020 akihiko moroi * * This software is released under the MIT License. * (See accompanying file LICENSE.txt or copy at http://opensource.org/licenses/MIT) */ #pragma once #include "CoreMinimal.h" #include "AssetTypeActions_Base.h" class FAssetTypeActions_GDMPlayerControllerProxyComponent : public FAssetTypeActions_Base { public: /* Begin IAssetTypeActions */ virtual FText GetName() const override; virtual FColor GetTypeColor() const override; virtual UClass* GetSupportedClass() const override; virtual uint32 GetCategories() override; virtual bool CanLocalize() const override; virtual FText GetAssetDescription(const FAssetData& AssetData) const override; /* End IAssetTypeActions */ }; ``` `SampleProject/Plugins/GameDebugMenu/Source/GameDebugMenuEditor/Public/AssetTypeActions/AssetTypeActions_GameDebugMenuManager.h`: ```h /** * Copyright (c) 2020 akihiko moroi * * This software is released under the MIT License. * (See accompanying file LICENSE.txt or copy at http://opensource.org/licenses/MIT) */ #pragma once #include "CoreMinimal.h" #include "AssetTypeActions_Base.h" class FAssetTypeActions_GameDebugMenuManager : public FAssetTypeActions_Base { public: /* Begin IAssetTypeActions */ virtual FText GetName() const override; virtual FColor GetTypeColor() const override; virtual UClass* GetSupportedClass() const override; virtual uint32 GetCategories() override; virtual bool CanLocalize() const override; virtual FText GetAssetDescription(const FAssetData& AssetData) const override; /* End IAssetTypeActions */ }; ``` `SampleProject/Plugins/GameDebugMenu/Source/GameDebugMenuEditor/Public/AssetTypeActions/AssetTypeActions_GameDebugMenuWidget.h`: ```h /** * Copyright (c) 2020 akihiko moroi * * This software is released under the MIT License. * (See accompanying file LICENSE.txt or copy at http://opensource.org/licenses/MIT) */ #pragma once #include "CoreMinimal.h" #include "AssetTypeActions_Base.h" class FAssetTypeActions_GameDebugMenuWidget : public FAssetTypeActions_Base { public: /* Begin IAssetTypeActions */ virtual FText GetName() const override; virtual FColor GetTypeColor() const override; virtual UClass* GetSupportedClass() const override; virtual uint32 GetCategories() override; virtual bool CanLocalize() const override; virtual FText GetAssetDescription(const FAssetData& AssetData) const override; /* End IAssetTypeActions */ }; ``` `SampleProject/Plugins/GameDebugMenu/Source/GameDebugMenuEditor/Public/Factory/GDMPlayerControllerProxyComponentFactory.h`: ```h /** * Copyright (c) 2020 akihiko moroi * * This software is released under the MIT License. * (See accompanying file LICENSE.txt or copy at http://opensource.org/licenses/MIT) */ #pragma once #include "Factories/Factory.h" #include "GDMPlayerControllerProxyComponentFactory.generated.h" #define LOCTEXT_NAMESPACE "GameDebugMenu" /** * */ UCLASS() class GAMEDEBUGMENUEDITOR_API UGDMPlayerControllerProxyComponentFactory : public UFactory { GENERATED_BODY() public: UGDMPlayerControllerProxyComponentFactory(const class FObjectInitializer& Object); virtual UObject* FactoryCreateNew(UClass* Class, UObject* InParent, FName Name, EObjectFlags Flags, UObject* Context, FFeedbackContext* Warn) override; }; #undef LOCTEXT_NAMESPACE ``` `SampleProject/Plugins/GameDebugMenu/Source/GameDebugMenuEditor/Public/Factory/GameDebugMenuManagerFactory.h`: ```h /** * Copyright (c) 2020 akihiko moroi * * This software is released under the MIT License. * (See accompanying file LICENSE.txt or copy at http://opensource.org/licenses/MIT) */ #pragma once #include "Factories/Factory.h" #include "GameDebugMenuManagerFactory.generated.h" #define LOCTEXT_NAMESPACE "GameDebugMenu" /** * */ UCLASS() class GAMEDEBUGMENUEDITOR_API UGameDebugMenuManagerFactory : public UFactory { GENERATED_BODY() public: UGameDebugMenuManagerFactory(const class FObjectInitializer& Object); virtual UObject* FactoryCreateNew(UClass* Class, UObject* InParent, FName Name, EObjectFlags Flags, UObject* Context, FFeedbackContext* Warn) override; }; #undef LOCTEXT_NAMESPACE ``` `SampleProject/Plugins/GameDebugMenu/Source/GameDebugMenuEditor/Public/Factory/GameDebugMenuWidgetFactory.h`: ```h /** * Copyright (c) 2020 akihiko moroi * * This software is released under the MIT License. * (See accompanying file LICENSE.txt or copy at http://opensource.org/licenses/MIT) */ #pragma once #include "Factories/Factory.h" #include "GameDebugMenuWidgetFactory.generated.h" #define LOCTEXT_NAMESPACE "GameDebugMenu" /** * */ UCLASS() class GAMEDEBUGMENUEDITOR_API UGameDebugMenuWidgetFactory : public UFactory { GENERATED_BODY() public: UGameDebugMenuWidgetFactory(const class FObjectInitializer& Object); virtual UObject* FactoryCreateNew(UClass* Class, UObject* InParent, FName Name, EObjectFlags Flags, UObject* Context, FFeedbackContext* Warn) override; }; #undef LOCTEXT_NAMESPACE ``` `SampleProject/Plugins/GameDebugMenu/Source/GameDebugMenuEditor/Public/GameDebugMenuEditor.h`: ```h /** * Copyright (c) 2020 akihiko moroi * * This software is released under the MIT License. * (See accompanying file LICENSE.txt or copy at http://opensource.org/licenses/MIT) */ #pragma once #include "Modules/ModuleManager.h" #include class IAssetTools; class IAssetTypeActions; class FGDMGameplayCategoryKeyPinFactory; class FGameDebugMenuEditorModule : public IModuleInterface { private: static EAssetTypeCategories::Type GDMAssetCategory; TArray< TSharedPtr > CreatedAssetTypeActions; TSharedPtr GameplayCategoryKeyPinFactory; public: /* Begin IModuleInterface */ virtual void StartupModule() override; virtual void ShutdownModule() override; /* End IModuleInterface */ public: static EAssetTypeCategories::Type GetAssetCategory(); private: void RegisterAssetTypeAction(IAssetTools& AssetTools, TSharedRef Action); }; ``` `SampleProject/SampleProject.uproject`: ```uproject { "FileVersion": 3, "EngineAssociation": "5.6", "Category": "", "Description": "", "Modules": [ { "Name": "SampleProject", "Type": "Runtime", "LoadingPhase": "Default", "AdditionalDependencies": [ "Engine" ] } ], "Plugins": [ { "Name": "GameplayAbilities", "Enabled": true } ] } ``` `SampleProject/Source/SampleProject.Target.cs`: ```cs // Fill out your copyright notice in the Description page of Project Settings. using UnrealBuildTool; using System.Collections.Generic; public class SampleProjectTarget : TargetRules { public SampleProjectTarget(TargetInfo Target) : base(Target) { Type = TargetType.Game; DefaultBuildSettings = BuildSettingsVersion.Latest; IncludeOrderVersion = EngineIncludeOrderVersion.Latest; CppStandard = CppStandardVersion.Latest; ExtraModuleNames.AddRange( new string[] { "SampleProject" } ); } } ``` `SampleProject/Source/SampleProject/MyActor.cpp`: ```cpp // Fill out your copyright notice in the Description page of Project Settings. #include "MyActor.h" static FAutoConsoleCommand TestCommand2 = FAutoConsoleCommand( TEXT("Sample.Command"), TEXT(""), FConsoleCommandWithArgsDelegate::CreateLambda([](const TArray& Args) { UE_LOG(LogTemp, Log, TEXT("call Sample.Command")); for (const FString& Arg : Args) { UE_LOG(LogTemp, Log, TEXT("arg : [%s]"), *Arg); } }) ); // Sets default values AMyActor::AMyActor() { // Set this actor to call Tick() every frame. You can turn this off to improve performance if you don't need it. PrimaryActorTick.bCanEverTick = true; } // Called when the game starts or when spawned void AMyActor::BeginPlay() { Super::BeginPlay(); } // Called every frame void AMyActor::Tick(float DeltaTime) { Super::Tick(DeltaTime); } ``` `SampleProject/Source/SampleProject/MyActor.h`: ```h // Fill out your copyright notice in the Description page of Project Settings. #pragma once #include "CoreMinimal.h" #include "GameFramework/Actor.h" #include "MyActor.generated.h" UCLASS() class SAMPLEPROJECT_API AMyActor : public AActor { GENERATED_BODY() public: // Sets default values for this actor's properties AMyActor(); protected: // Called when the game starts or when spawned virtual void BeginPlay() override; public: // Called every frame virtual void Tick(float DeltaTime) override; }; ``` `SampleProject/Source/SampleProject/SampleProject.Build.cs`: ```cs // Fill out your copyright notice in the Description page of Project Settings. using UnrealBuildTool; public class SampleProject : ModuleRules { public SampleProject(ReadOnlyTargetRules Target) : base(Target) { PCHUsage = PCHUsageMode.UseExplicitOrSharedPCHs; PublicDependencyModuleNames.AddRange(new string[] { "Core", "CoreUObject", "Engine", "InputCore" }); PrivateDependencyModuleNames.AddRange(new string[] { }); // Uncomment if you are using Slate UI // PrivateDependencyModuleNames.AddRange(new string[] { "Slate", "SlateCore" }); // Uncomment if you are using online features // PrivateDependencyModuleNames.Add("OnlineSubsystem"); // To include OnlineSubsystemSteam, add it to the plugins section in your uproject file with the Enabled attribute set to true } } ``` `SampleProject/Source/SampleProject/SampleProject.cpp`: ```cpp // Fill out your copyright notice in the Description page of Project Settings. #include "SampleProject.h" #include "Modules/ModuleManager.h" IMPLEMENT_PRIMARY_GAME_MODULE( FDefaultGameModuleImpl, SampleProject, "SampleProject" ); ``` `SampleProject/Source/SampleProject/SampleProject.h`: ```h // Fill out your copyright notice in the Description page of Project Settings. #pragma once #include "CoreMinimal.h" ``` `SampleProject/Source/SampleProjectEditor.Target.cs`: ```cs // Fill out your copyright notice in the Description page of Project Settings. using UnrealBuildTool; using System.Collections.Generic; public class SampleProjectEditorTarget : TargetRules { public SampleProjectEditorTarget(TargetInfo Target) : base(Target) { Type = TargetType.Editor; DefaultBuildSettings = BuildSettingsVersion.Latest; IncludeOrderVersion = EngineIncludeOrderVersion.Latest; ExtraModuleNames.AddRange( new string[] { "SampleProject" } ); } } ```