feat(debug): add runtime debug monitor and editor UI

main
uc-hoba 3 weeks ago
parent 23d3df8973
commit 8a579a7d29
  1. 555
      Assets/Main.unity
  2. 308
      Packages/com.ultracombos.giant-museum/Editor/DebugMonitorHUDEditor.cs
  3. 2
      Packages/com.ultracombos.giant-museum/Editor/DebugMonitorHUDEditor.cs.meta
  4. 105
      Packages/com.ultracombos.giant-museum/Editor/DebugSettingsGUIEditor.cs
  5. 285
      Packages/com.ultracombos.giant-museum/Runtime/DebugMonitorHUD.cs
  6. 2
      Packages/com.ultracombos.giant-museum/Runtime/DebugMonitorHUD.cs.meta
  7. 102
      Packages/com.ultracombos.giant-museum/Runtime/DebugSettingsGUI.cs
  8. 2
      Packages/com.ultracombos.giant-museum/Runtime/DisplayConfig.cs
  9. 3
      Packages/com.ultracombos.giant-museum/Runtime/UltraCombos.GiantMuseum.Runtime.asmdef
  10. 4
      Unity-25014-ReadySetRide.slnx

@ -151,6 +151,143 @@ Transform:
- {fileID: 520906050}
m_Father: {fileID: 465598214}
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
--- !u!1 &139439301
GameObject:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
serializedVersion: 6
m_Component:
- component: {fileID: 139439302}
- component: {fileID: 139439304}
- component: {fileID: 139439303}
m_Layer: 5
m_Name: Title
m_TagString: Untagged
m_Icon: {fileID: 0}
m_NavMeshLayer: 0
m_StaticEditorFlags: 0
m_IsActive: 1
--- !u!224 &139439302
RectTransform:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 139439301}
m_LocalRotation: {x: -0, y: -0, z: -0, w: 1}
m_LocalPosition: {x: 0, y: 0, z: 0}
m_LocalScale: {x: 1, y: 1, z: 1}
m_ConstrainProportionsScale: 0
m_Children: []
m_Father: {fileID: 1173268650}
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
m_AnchorMin: {x: 0, y: 0}
m_AnchorMax: {x: 0, y: 0}
m_AnchoredPosition: {x: 0, y: 0}
m_SizeDelta: {x: 0, y: 24.2}
m_Pivot: {x: 0.5, y: 0.5}
--- !u!114 &139439303
MonoBehaviour:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 139439301}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: f4688fdb7df04437aeb418b961361dc5, type: 3}
m_Name:
m_EditorClassIdentifier: Unity.TextMeshPro::TMPro.TextMeshProUGUI
m_Material: {fileID: 0}
m_Color: {r: 1, g: 1, b: 1, a: 1}
m_RaycastTarget: 1
m_RaycastPadding: {x: 0, y: 0, z: 0, w: 0}
m_Maskable: 1
m_OnCullStateChanged:
m_PersistentCalls:
m_Calls: []
m_text: Ready, Set Ride!
m_isRightToLeft: 0
m_fontAsset: {fileID: 11400000, guid: 8f586378b4e144a9851e7b34d9b748ee, type: 2}
m_sharedMaterial: {fileID: 2180264, guid: 8f586378b4e144a9851e7b34d9b748ee, type: 2}
m_fontSharedMaterials: []
m_fontMaterial: {fileID: 0}
m_fontMaterials: []
m_fontColor32:
serializedVersion: 2
rgba: 4294967295
m_fontColor: {r: 1, g: 1, b: 1, a: 1}
m_enableVertexGradient: 0
m_colorMode: 3
m_fontColorGradient:
topLeft: {r: 1, g: 1, b: 1, a: 1}
topRight: {r: 1, g: 1, b: 1, a: 1}
bottomLeft: {r: 1, g: 1, b: 1, a: 1}
bottomRight: {r: 1, g: 1, b: 1, a: 1}
m_fontColorGradientPreset: {fileID: 0}
m_spriteAsset: {fileID: 0}
m_tintAllSprites: 0
m_StyleSheet: {fileID: 0}
m_TextStyleHashCode: -1183493901
m_overrideHtmlColors: 0
m_faceColor:
serializedVersion: 2
rgba: 4294967295
m_fontSize: 24
m_fontSizeBase: 24
m_fontWeight: 400
m_enableAutoSizing: 0
m_fontSizeMin: 18
m_fontSizeMax: 72
m_fontStyle: 0
m_HorizontalAlignment: 1
m_VerticalAlignment: 256
m_textAlignment: 65535
m_characterSpacing: 0
m_characterHorizontalScale: 1
m_wordSpacing: 0
m_lineSpacing: 0
m_lineSpacingMax: 0
m_paragraphSpacing: 0
m_charWidthMaxAdj: 0
m_TextWrappingMode: 1
m_wordWrappingRatios: 0.4
m_overflowMode: 0
m_linkedTextComponent: {fileID: 0}
parentLinkedComponent: {fileID: 0}
m_enableKerning: 0
m_ActiveFontFeatures: 6e72656b
m_enableExtraPadding: 0
checkPaddingRequired: 0
m_isRichText: 1
m_EmojiFallbackSupport: 1
m_parseCtrlCharacters: 1
m_isOrthographic: 1
m_isCullingEnabled: 0
m_horizontalMapping: 0
m_verticalMapping: 0
m_uvLineOffset: 0
m_geometrySortingOrder: 0
m_IsTextObjectScaleStatic: 0
m_VertexBufferAutoSizeReduction: 0
m_useMaxVisibleDescender: 1
m_pageToDisplay: 1
m_margin: {x: 0, y: 0, z: 0, w: 0}
m_isUsingLegacyAnimationComponent: 0
m_isVolumetricText: 0
m_hasFontAssetChanged: 0
m_baseMaterial: {fileID: 0}
m_maskOffset: {x: 0, y: 0, z: 0, w: 0}
--- !u!222 &139439304
CanvasRenderer:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 139439301}
m_CullTransparentMesh: 1
--- !u!1 &203844586
GameObject:
m_ObjectHideFlags: 0
@ -271,13 +408,13 @@ Transform:
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 203844586}
serializedVersion: 2
m_LocalRotation: {x: 0.30044645, y: 0.3693323, z: -0.12753193, w: 0.87009263}
m_LocalRotation: {x: 0.28604206, y: -0.30899116, z: 0.09801777, w: 0.9017189}
m_LocalPosition: {x: 0, y: 3, z: 0}
m_LocalScale: {x: 1, y: 1, z: 1}
m_ConstrainProportionsScale: 0
m_Children: []
m_Father: {fileID: 604712594}
m_LocalEulerAnglesHint: {x: 38.1, y: 46, z: 0}
m_LocalEulerAnglesHint: {x: 35.2, y: -37.83, z: 0}
--- !u!1001 &283971856
PrefabInstance:
m_ObjectHideFlags: 0
@ -769,6 +906,258 @@ Transform:
- {fileID: 924830808}
m_Father: {fileID: 76224412}
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
--- !u!1 &587767517
GameObject:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
serializedVersion: 6
m_Component:
- component: {fileID: 587767518}
- component: {fileID: 587767521}
- component: {fileID: 587767520}
- component: {fileID: 587767519}
m_Layer: 5
m_Name: Info
m_TagString: Untagged
m_Icon: {fileID: 0}
m_NavMeshLayer: 0
m_StaticEditorFlags: 0
m_IsActive: 1
--- !u!224 &587767518
RectTransform:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 587767517}
m_LocalRotation: {x: -0, y: -0, z: -0, w: 1}
m_LocalPosition: {x: 0, y: 0, z: 0}
m_LocalScale: {x: 1, y: 1, z: 1}
m_ConstrainProportionsScale: 0
m_Children: []
m_Father: {fileID: 1173268650}
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
m_AnchorMin: {x: 0, y: 0}
m_AnchorMax: {x: 0, y: 0}
m_AnchoredPosition: {x: 0, y: 0}
m_SizeDelta: {x: 0, y: 50}
m_Pivot: {x: 0.5, y: 0.5}
--- !u!114 &587767519
MonoBehaviour:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 587767517}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: f4688fdb7df04437aeb418b961361dc5, type: 3}
m_Name:
m_EditorClassIdentifier: Unity.TextMeshPro::TMPro.TextMeshProUGUI
m_Material: {fileID: 0}
m_Color: {r: 1, g: 1, b: 1, a: 1}
m_RaycastTarget: 1
m_RaycastPadding: {x: 0, y: 0, z: 0, w: 0}
m_Maskable: 1
m_OnCullStateChanged:
m_PersistentCalls:
m_Calls: []
m_text: '<b><color=#FFC940>Display</color></b>
<color=#AAAAAA>Current Fps</color>
<color=#7EC8E3>30</color>
<b><color=#FFC940>FMCR</color></b>
<color=#AAAAAA>Distance</color>
<color=#7EC8E3>65</color>
<color=#AAAAAA>Horizontal</color> <color=#7EC8E3>-7.5</color>
<color=#AAAAAA>Vertical</color>
<color=#7EC8E3>15</color>
'
m_isRightToLeft: 0
m_fontAsset: {fileID: 11400000, guid: 52d992bef73842844aaf4142ab3f3f5d, type: 2}
m_sharedMaterial: {fileID: 6824989588225411414, guid: 52d992bef73842844aaf4142ab3f3f5d, type: 2}
m_fontSharedMaterials: []
m_fontMaterial: {fileID: 0}
m_fontMaterials: []
m_fontColor32:
serializedVersion: 2
rgba: 4294967295
m_fontColor: {r: 1, g: 1, b: 1, a: 1}
m_enableVertexGradient: 0
m_colorMode: 3
m_fontColorGradient:
topLeft: {r: 1, g: 1, b: 1, a: 1}
topRight: {r: 1, g: 1, b: 1, a: 1}
bottomLeft: {r: 1, g: 1, b: 1, a: 1}
bottomRight: {r: 1, g: 1, b: 1, a: 1}
m_fontColorGradientPreset: {fileID: 0}
m_spriteAsset: {fileID: 0}
m_tintAllSprites: 0
m_StyleSheet: {fileID: 0}
m_TextStyleHashCode: -1183493901
m_overrideHtmlColors: 0
m_faceColor:
serializedVersion: 2
rgba: 4294967295
m_fontSize: 14
m_fontSizeBase: 14
m_fontWeight: 400
m_enableAutoSizing: 0
m_fontSizeMin: 18
m_fontSizeMax: 72
m_fontStyle: 0
m_HorizontalAlignment: 1
m_VerticalAlignment: 256
m_textAlignment: 65535
m_characterSpacing: 0
m_characterHorizontalScale: 1
m_wordSpacing: 0
m_lineSpacing: 12
m_lineSpacingMax: 0
m_paragraphSpacing: 0
m_charWidthMaxAdj: 0
m_TextWrappingMode: 1
m_wordWrappingRatios: 0.4
m_overflowMode: 0
m_linkedTextComponent: {fileID: 0}
parentLinkedComponent: {fileID: 0}
m_enableKerning: 0
m_ActiveFontFeatures: 6e72656b
m_enableExtraPadding: 0
checkPaddingRequired: 0
m_isRichText: 1
m_EmojiFallbackSupport: 1
m_parseCtrlCharacters: 1
m_isOrthographic: 1
m_isCullingEnabled: 0
m_horizontalMapping: 0
m_verticalMapping: 0
m_uvLineOffset: 0
m_geometrySortingOrder: 0
m_IsTextObjectScaleStatic: 0
m_VertexBufferAutoSizeReduction: 0
m_useMaxVisibleDescender: 1
m_pageToDisplay: 1
m_margin: {x: 0, y: 0, z: 0, w: 0}
m_isUsingLegacyAnimationComponent: 0
m_isVolumetricText: 0
m_hasFontAssetChanged: 0
m_baseMaterial: {fileID: 0}
m_maskOffset: {x: 0, y: 0, z: 0, w: 0}
--- !u!114 &587767520
MonoBehaviour:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 587767517}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: 705242ff475ca7b4bbc7341c5c7218ac, type: 3}
m_Name:
m_EditorClassIdentifier: UltraCombos.GiantMuseum.Runtime::UltraCombos.DebugMonitorHUD
displayText: {fileID: 587767519}
refreshRate: 30
entries:
- target: {fileID: 476511607}
groupLabel: Display
fields:
- CurrentFps
- target: {fileID: 465598213}
groupLabel: FMCR
fields:
- distance
- horizontal
- vertical
--- !u!222 &587767521
CanvasRenderer:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 587767517}
m_CullTransparentMesh: 1
--- !u!1 &588558984
GameObject:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
serializedVersion: 6
m_Component:
- component: {fileID: 588558985}
- component: {fileID: 588558987}
- component: {fileID: 588558986}
m_Layer: 5
m_Name: Output
m_TagString: Untagged
m_Icon: {fileID: 0}
m_NavMeshLayer: 0
m_StaticEditorFlags: 0
m_IsActive: 1
--- !u!224 &588558985
RectTransform:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 588558984}
m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}
m_LocalPosition: {x: 0, y: 0, z: 0}
m_LocalScale: {x: 1, y: 1, z: 1}
m_ConstrainProportionsScale: 0
m_Children: []
m_Father: {fileID: 2020615927}
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
m_AnchorMin: {x: 0.5, y: 0.5}
m_AnchorMax: {x: 0.5, y: 0.5}
m_AnchoredPosition: {x: 0, y: 0}
m_SizeDelta: {x: 1080, y: 1920}
m_Pivot: {x: 0.5, y: 0.5}
--- !u!114 &588558986
MonoBehaviour:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 588558984}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: 1344c3c82d62a2a41a3576d8abb8e3ea, type: 3}
m_Name:
m_EditorClassIdentifier: UnityEngine.UI::UnityEngine.UI.RawImage
m_Material: {fileID: 0}
m_Color: {r: 1, g: 1, b: 1, a: 1}
m_RaycastTarget: 1
m_RaycastPadding: {x: 0, y: 0, z: 0, w: 0}
m_Maskable: 1
m_OnCullStateChanged:
m_PersistentCalls:
m_Calls: []
m_Texture: {fileID: 8400000, guid: de3934cb10406bb4daa84cabd7a9a4d8, type: 2}
m_UVRect:
serializedVersion: 2
x: 0
y: 0
width: 1
height: 1
--- !u!222 &588558987
CanvasRenderer:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 588558984}
m_CullTransparentMesh: 1
--- !u!1 &604712593
GameObject:
m_ObjectHideFlags: 0
@ -1466,6 +1855,70 @@ BoxCollider:
serializedVersion: 3
m_Size: {x: 1, y: 1, z: 1}
m_Center: {x: 0, y: 0, z: 0}
--- !u!1 &1173268649
GameObject:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
serializedVersion: 6
m_Component:
- component: {fileID: 1173268650}
- component: {fileID: 1173268651}
m_Layer: 5
m_Name: Group
m_TagString: Untagged
m_Icon: {fileID: 0}
m_NavMeshLayer: 0
m_StaticEditorFlags: 0
m_IsActive: 1
--- !u!224 &1173268650
RectTransform:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 1173268649}
m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}
m_LocalPosition: {x: 0, y: 0, z: 0}
m_LocalScale: {x: 1, y: 1, z: 1}
m_ConstrainProportionsScale: 0
m_Children:
- {fileID: 139439302}
- {fileID: 587767518}
m_Father: {fileID: 1788175773}
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
m_AnchorMin: {x: 0, y: 1}
m_AnchorMax: {x: 0, y: 1}
m_AnchoredPosition: {x: 32, y: -32}
m_SizeDelta: {x: 301.5, y: 0}
m_Pivot: {x: 0, y: 1}
--- !u!114 &1173268651
MonoBehaviour:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 1173268649}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: 59f8146938fff824cb5fd77236b75775, type: 3}
m_Name:
m_EditorClassIdentifier: UnityEngine.UI::UnityEngine.UI.VerticalLayoutGroup
m_Padding:
m_Left: 0
m_Right: 0
m_Top: 0
m_Bottom: 0
m_ChildAlignment: 0
m_Spacing: 16.7
m_ChildForceExpandWidth: 1
m_ChildForceExpandHeight: 1
m_ChildControlWidth: 1
m_ChildControlHeight: 0
m_ChildScaleWidth: 0
m_ChildScaleHeight: 0
m_ReverseArrangement: 0
--- !u!1 &1182891853
GameObject:
m_ObjectHideFlags: 0
@ -1482,7 +1935,7 @@ GameObject:
m_Icon: {fileID: 0}
m_NavMeshLayer: 0
m_StaticEditorFlags: 0
m_IsActive: 1
m_IsActive: 0
--- !u!114 &1182891854
MonoBehaviour:
m_ObjectHideFlags: 0
@ -1765,6 +2218,8 @@ RectTransform:
m_Children:
- {fileID: 403055229}
- {fileID: 1889754489}
- {fileID: 2020615927}
- {fileID: 1173268650}
m_Father: {fileID: 303688530}
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
m_AnchorMin: {x: 0, y: 0}
@ -1830,7 +2285,7 @@ Canvas:
m_OverridePixelPerfect: 0
m_SortingBucketNormalizedSize: 0
m_VertexColorAlwaysGammaSpace: 0
m_AdditionalShaderChannelsFlag: 0
m_AdditionalShaderChannelsFlag: 25
m_UpdateRectTransformForStandalone: 0
m_SortingLayerID: 0
m_SortingOrder: 0
@ -1871,7 +2326,7 @@ RectTransform:
m_AnchorMin: {x: 1, y: 0}
m_AnchorMax: {x: 1, y: 1}
m_AnchoredPosition: {x: 0, y: 0}
m_SizeDelta: {x: 608.58, y: 0}
m_SizeDelta: {x: 533, y: -64}
m_Pivot: {x: 1, y: 0.5}
--- !u!114 &1889754490
MonoBehaviour:
@ -1974,6 +2429,96 @@ Transform:
m_CorrespondingSourceObject: {fileID: 2259651201424232592, guid: 2419b212eec718e4c8153284ca64a8a5, type: 3}
m_PrefabInstance: {fileID: 1992328036}
m_PrefabAsset: {fileID: 0}
--- !u!1 &2020615926
GameObject:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
serializedVersion: 6
m_Component:
- component: {fileID: 2020615927}
- component: {fileID: 2020615930}
- component: {fileID: 2020615929}
- component: {fileID: 2020615928}
m_Layer: 5
m_Name: RawImage (1)
m_TagString: Untagged
m_Icon: {fileID: 0}
m_NavMeshLayer: 0
m_StaticEditorFlags: 0
m_IsActive: 1
--- !u!224 &2020615927
RectTransform:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 2020615926}
m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}
m_LocalPosition: {x: 0, y: 0, z: 0}
m_LocalScale: {x: 1, y: 1, z: 1}
m_ConstrainProportionsScale: 0
m_Children:
- {fileID: 588558985}
m_Father: {fileID: 1788175773}
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
m_AnchorMin: {x: 1, y: 0}
m_AnchorMax: {x: 1, y: 1}
m_AnchoredPosition: {x: -535, y: 0}
m_SizeDelta: {x: 756, y: -64}
m_Pivot: {x: 1, y: 0.5}
--- !u!114 &2020615928
MonoBehaviour:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 2020615926}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: fe87c0e1cc204ed48ad3b37840f39efc, type: 3}
m_Name:
m_EditorClassIdentifier: UnityEngine.UI::UnityEngine.UI.Image
m_Material: {fileID: 0}
m_Color: {r: 1, g: 1, b: 1, a: 1}
m_RaycastTarget: 1
m_RaycastPadding: {x: 0, y: 0, z: 0, w: 0}
m_Maskable: 1
m_OnCullStateChanged:
m_PersistentCalls:
m_Calls: []
m_Sprite: {fileID: 0}
m_Type: 0
m_PreserveAspect: 0
m_FillCenter: 1
m_FillMethod: 4
m_FillAmount: 1
m_FillClockwise: 1
m_FillOrigin: 0
m_UseSpriteMesh: 0
m_PixelsPerUnitMultiplier: 1
--- !u!114 &2020615929
MonoBehaviour:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 2020615926}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: 31a19414c41e5ae4aae2af33fee712f6, type: 3}
m_Name:
m_EditorClassIdentifier: UnityEngine.UI::UnityEngine.UI.Mask
m_ShowMaskGraphic: 0
--- !u!222 &2020615930
CanvasRenderer:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 2020615926}
m_CullTransparentMesh: 1
--- !u!1 &2116198337
GameObject:
m_ObjectHideFlags: 0

@ -0,0 +1,308 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using UnityEditor;
using UnityEngine;
namespace UltraCombos
{
[CustomEditor(typeof(DebugMonitorHUD))]
public class DebugMonitorHUDEditor : Editor
{
// ── Supported member types ────────────────────────────────────────────────
private static readonly HashSet<Type> supportedTypes = new()
{
typeof(bool), typeof(int), typeof(float),
typeof(string), typeof(Vector2), typeof(Vector3), typeof(Color)
};
private static bool IsSupported(Type t) => supportedTypes.Contains(t) || t.IsEnum;
// ── Cached SerializedProperties ───────────────────────────────────────────
private SerializedProperty displayTextProp;
private SerializedProperty refreshRateProp;
private void OnEnable()
{
displayTextProp = serializedObject.FindProperty("displayText");
refreshRateProp = serializedObject.FindProperty("refreshRate");
}
// ── Inspector ─────────────────────────────────────────────────────────────
public override void OnInspectorGUI()
{
var hud = (DebugMonitorHUD)target;
serializedObject.Update();
EditorGUILayout.LabelField("Display", EditorStyles.boldLabel);
EditorGUILayout.PropertyField(displayTextProp, new GUIContent("Display Text",
"The TextMeshProUGUI component that will show the monitored values."));
EditorGUILayout.Space(4);
EditorGUILayout.LabelField("General", EditorStyles.boldLabel);
EditorGUILayout.PropertyField(refreshRateProp, new GUIContent("Refresh Rate"));
serializedObject.ApplyModifiedProperties();
EditorGUILayout.Space(10);
EditorGUILayout.LabelField("Entries", EditorStyles.boldLabel);
for (var i = 0; i < hud.entries.Count; i++)
{
var removed = DrawEntry(hud, hud.entries[i], i);
if (removed)
{
Undo.RecordObject(hud, "Remove Entry");
hud.entries.RemoveAt(i);
EditorUtility.SetDirty(hud);
i--;
}
EditorGUILayout.Space(2);
}
EditorGUILayout.Space(4);
if (GUILayout.Button("+ Add Entry", GUILayout.Height(26)))
{
Undo.RecordObject(hud, "Add Entry");
hud.entries.Add(new DebugMonitorHUD.Entry());
EditorUtility.SetDirty(hud);
}
}
// ── Draw one Entry — returns true when the user clicked Remove ─────────────
private static bool DrawEntry(DebugMonitorHUD hud, DebugMonitorHUD.Entry entry, int index)
{
var removed = false;
using (new EditorGUILayout.VerticalScope(EditorStyles.helpBox))
{
// Header row
using (new EditorGUILayout.HorizontalScope())
{
var title = string.IsNullOrEmpty(entry.groupLabel)
? $"Entry {index}"
: $"Entry {index} — {entry.groupLabel}";
EditorGUILayout.LabelField(title, EditorStyles.boldLabel);
GUILayout.FlexibleSpace();
if (GUILayout.Button("✕", GUILayout.Width(22), GUILayout.Height(18)))
{
removed = true;
}
}
// Group label
EditorGUI.BeginChangeCheck();
var newLabel = EditorGUILayout.TextField("Group Label", entry.groupLabel);
if (EditorGUI.EndChangeCheck())
{
Undo.RecordObject(hud, "Edit Group Label");
entry.groupLabel = newLabel;
EditorUtility.SetDirty(hud);
}
// Target object
EditorGUI.BeginChangeCheck();
var newTarget = EditorGUILayout.ObjectField(
new GUIContent("Target",
"Drag any Component or ScriptableObject here.\n" +
"Dropping a GameObject picks its first non-Transform Component."),
entry.target,
typeof(UnityEngine.Object),
allowSceneObjects: true);
if (EditorGUI.EndChangeCheck())
{
Undo.RecordObject(hud, "Set Target");
if (newTarget is GameObject go)
{
var comps = go.GetComponents<Component>();
newTarget = comps.FirstOrDefault(c => c is not Transform) ??
comps.FirstOrDefault() ??
(UnityEngine.Object)go;
}
entry.target = newTarget;
entry.fields.Clear();
EditorUtility.SetDirty(hud);
}
// Member selection
if (entry.target != null)
{
DrawMemberSelection(hud, entry);
}
}
return removed;
}
// ── Member checkboxes ─────────────────────────────────────────────────────
private static void DrawMemberSelection(DebugMonitorHUD hud, DebugMonitorHUD.Entry entry)
{
var type = entry.target.GetType();
var members = CollectMembers(type);
if (members.Count == 0)
{
EditorGUILayout.HelpBox(
"No displayable fields or properties found on this object.\n" +
"Supported types: bool · int · float · string · Vector2 · Vector3 · Color · Enum\n" +
"Fields must be public or [SerializeField]. Properties must be public with a getter.",
MessageType.Info);
return;
}
EditorGUILayout.Space(4);
// Select All / None
using (new EditorGUILayout.HorizontalScope())
{
EditorGUILayout.LabelField("Members to Display", EditorStyles.boldLabel);
GUILayout.FlexibleSpace();
if (GUILayout.Button("All", GUILayout.Width(36)))
{
Undo.RecordObject(hud, "Select All Members");
entry.fields = members.Select(m => m.Name).ToList();
EditorUtility.SetDirty(hud);
}
if (GUILayout.Button("None", GUILayout.Width(38)))
{
Undo.RecordObject(hud, "Deselect All Members");
entry.fields.Clear();
EditorUtility.SetDirty(hud);
}
}
// Per-member toggles
EditorGUI.indentLevel++;
foreach (var mi in members)
{
var isSelected = entry.fields.Contains(mi.Name);
var niceName = ObjectNames.NicifyVariableName(mi.Name);
var memberType = GetMemberType(mi);
var typeName = FriendlyType(memberType);
var hasRange = mi.GetCustomAttribute<RangeAttribute>() != null;
var isProperty = mi is PropertyInfo;
var suffix = (isProperty ? ", prop" : string.Empty) +
(hasRange ? ", Range" : string.Empty);
var label = new GUIContent(
$"{niceName} ({typeName}{suffix})",
tooltip: $"{(isProperty ? "Property" : "Field")}: {mi.Name}\nType: {memberType.FullName}");
EditorGUI.BeginChangeCheck();
var next = EditorGUILayout.ToggleLeft(label, isSelected);
if (EditorGUI.EndChangeCheck())
{
Undo.RecordObject(hud, next ? "Add Member" : "Remove Member");
if (next)
{
entry.fields.Add(mi.Name);
}
else
{
entry.fields.Remove(mi.Name);
}
EditorUtility.SetDirty(hud);
}
}
EditorGUI.indentLevel--;
}
// ── Reflection helpers ────────────────────────────────────────────────────
private static List<MemberInfo> CollectMembers(Type type)
{
const BindingFlags fieldFlags =
BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance;
const BindingFlags propFlags =
BindingFlags.Public | BindingFlags.Instance;
var result = new List<MemberInfo>();
for (var t = type; t != null && !IsUnityBuiltIn(t); t = t.BaseType)
{
foreach (var fi in t.GetFields(fieldFlags | BindingFlags.DeclaredOnly))
{
// Skip compiler-generated backing fields (e.g. <Prop>k__BackingField).
if (fi.Name[0] == '<')
{
continue;
}
var exposed = fi.IsPublic || fi.GetCustomAttribute<SerializeField>() != null;
if (!exposed)
{
continue;
}
if (fi.GetCustomAttribute<HideInInspector>() != null)
{
continue;
}
if (!IsSupported(fi.FieldType))
{
continue;
}
if (!result.Any(r => r.Name == fi.Name))
{
result.Add(fi);
}
}
foreach (var pi in t.GetProperties(propFlags | BindingFlags.DeclaredOnly))
{
// Must have a getter and no index parameters.
if (!pi.CanRead || pi.GetIndexParameters().Length > 0)
{
continue;
}
if (pi.GetCustomAttribute<HideInInspector>() != null)
{
continue;
}
if (!IsSupported(pi.PropertyType))
{
continue;
}
if (!result.Any(r => r.Name == pi.Name))
{
result.Add(pi);
}
}
}
return result;
}
private static Type GetMemberType(MemberInfo mi) =>
mi is FieldInfo fi ? fi.FieldType : ((PropertyInfo)mi).PropertyType;
private static bool IsUnityBuiltIn(Type t) =>
t.Namespace != null &&
t.Namespace.StartsWith("UnityEngine", StringComparison.Ordinal);
private static string FriendlyType(Type t)
{
if (t == typeof(float)) { return "float"; }
if (t == typeof(int)) { return "int"; }
if (t == typeof(bool)) { return "bool"; }
if (t == typeof(string)) { return "string"; }
if (t == typeof(Vector2)) { return "Vector2"; }
if (t == typeof(Vector3)) { return "Vector3"; }
if (t == typeof(Color)) { return "Color"; }
if (t.IsEnum) { return t.Name; }
return t.Name;
}
}
}

@ -0,0 +1,2 @@
fileFormatVersion: 2
guid: b9de48d136769a442bc787083085d69d

@ -10,7 +10,7 @@ namespace UltraCombos
[CustomEditor(typeof(DebugSettingsGUI))]
public class DebugSettingsGUIEditor : Editor
{
// ── Supported field types ─────────────────────────────────────────────────
// ── Supported member types ────────────────────────────────────────────────
private static readonly HashSet<Type> supportedTypes = new()
{
@ -27,7 +27,7 @@ namespace UltraCombos
private void OnEnable()
{
toggleKeyProp = serializedObject.FindProperty("toggleKey");
toggleKeyProp = serializedObject.FindProperty("toggleKey");
initialRectProp = serializedObject.FindProperty("initialRect");
}
@ -39,7 +39,7 @@ namespace UltraCombos
serializedObject.Update();
EditorGUILayout.LabelField("General", EditorStyles.boldLabel);
EditorGUILayout.PropertyField(toggleKeyProp, new GUIContent("Toggle Key"));
EditorGUILayout.PropertyField(toggleKeyProp, new GUIContent("Toggle Key"));
EditorGUILayout.PropertyField(initialRectProp, new GUIContent("Initial Rect"));
serializedObject.ApplyModifiedProperties();
@ -102,9 +102,7 @@ namespace UltraCombos
EditorUtility.SetDirty(gui);
}
// Target object.
// Drag any Component or ScriptableObject here.
// Dropping a GameObject picks its first non-Transform Component automatically.
// Target object
EditorGUI.BeginChangeCheck();
var newTarget = EditorGUILayout.ObjectField(
new GUIContent("Target",
@ -131,29 +129,29 @@ namespace UltraCombos
EditorUtility.SetDirty(gui);
}
// Field selection
// Member selection
if (entry.target != null)
{
DrawFieldSelection(gui, entry);
DrawMemberSelection(gui, entry);
}
}
return removed;
}
// ── Field checkboxes ──────────────────────────────────────────────────────
// ── Member checkboxes ─────────────────────────────────────────────────────
private static void DrawFieldSelection(DebugSettingsGUI gui, DebugSettingsGUI.Entry entry)
private static void DrawMemberSelection(DebugSettingsGUI gui, DebugSettingsGUI.Entry entry)
{
var type = entry.target.GetType();
var fields = CollectFields(type);
var members = CollectMembers(type);
if (fields.Count == 0)
if (members.Count == 0)
{
EditorGUILayout.HelpBox(
"No displayable fields found on this object.\n" +
"No displayable fields or properties found on this object.\n" +
"Supported types: bool · int · float · string · Vector2 · Vector3 · Color · Enum\n" +
"Fields must be public or marked [SerializeField].",
"Fields must be public or [SerializeField]. Properties must be public with a getter.",
MessageType.Info);
return;
}
@ -163,50 +161,54 @@ namespace UltraCombos
// Select All / None
using (new EditorGUILayout.HorizontalScope())
{
EditorGUILayout.LabelField("Fields to Display", EditorStyles.boldLabel);
EditorGUILayout.LabelField("Members to Display", EditorStyles.boldLabel);
GUILayout.FlexibleSpace();
if (GUILayout.Button("All", GUILayout.Width(36)))
{
Undo.RecordObject(gui, "Select All Fields");
entry.fields = fields.Select(f => f.Name).ToList();
Undo.RecordObject(gui, "Select All Members");
entry.fields = members.Select(m => m.Name).ToList();
EditorUtility.SetDirty(gui);
}
if (GUILayout.Button("None", GUILayout.Width(38)))
{
Undo.RecordObject(gui, "Deselect All Fields");
Undo.RecordObject(gui, "Deselect All Members");
entry.fields.Clear();
EditorUtility.SetDirty(gui);
}
}
// Per-field toggles
// Per-member toggles
EditorGUI.indentLevel++;
foreach (var fi in fields)
foreach (var mi in members)
{
var isSelected = entry.fields.Contains(fi.Name);
var niceName = ObjectNames.NicifyVariableName(fi.Name);
var typeName = FriendlyType(fi.FieldType);
var hasRange = fi.GetCustomAttribute<RangeAttribute>() != null;
var isSelected = entry.fields.Contains(mi.Name);
var niceName = ObjectNames.NicifyVariableName(mi.Name);
var memberType = GetMemberType(mi);
var typeName = FriendlyType(memberType);
var hasRange = mi.GetCustomAttribute<RangeAttribute>() != null;
var isProperty = mi is PropertyInfo;
var suffix = (isProperty ? ", prop" : string.Empty) +
(hasRange ? ", Range" : string.Empty);
var label = new GUIContent(
$"{niceName} ({typeName}{(hasRange ? ", Range" : string.Empty)})",
tooltip: $"Field: {fi.Name}\nType: {fi.FieldType.FullName}");
$"{niceName} ({typeName}{suffix})",
tooltip: $"{(isProperty ? "Property" : "Field")}: {mi.Name}\nType: {memberType.FullName}");
EditorGUI.BeginChangeCheck();
var next = EditorGUILayout.ToggleLeft(label, isSelected);
if (EditorGUI.EndChangeCheck())
{
Undo.RecordObject(gui, next ? "Add Field" : "Remove Field");
Undo.RecordObject(gui, next ? "Add Member" : "Remove Member");
if (next)
{
entry.fields.Add(fi.Name);
entry.fields.Add(mi.Name);
}
else
{
entry.fields.Remove(fi.Name);
entry.fields.Remove(mi.Name);
}
EditorUtility.SetDirty(gui);
}
@ -217,17 +219,24 @@ namespace UltraCombos
// ── Reflection helpers ────────────────────────────────────────────────────
private static List<FieldInfo> CollectFields(Type type)
private static List<MemberInfo> CollectMembers(Type type)
{
const BindingFlags flags =
const BindingFlags fieldFlags =
BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance;
const BindingFlags propFlags =
BindingFlags.Public | BindingFlags.Instance;
var result = new List<FieldInfo>();
var result = new List<MemberInfo>();
for (var t = type; t != null && t != typeof(UnityEngine.Object); t = t.BaseType)
for (var t = type; t != null && !IsUnityBuiltIn(t); t = t.BaseType)
{
foreach (var fi in t.GetFields(flags | BindingFlags.DeclaredOnly))
foreach (var fi in t.GetFields(fieldFlags | BindingFlags.DeclaredOnly))
{
// Skip compiler-generated backing fields (e.g. <Prop>k__BackingField).
if (fi.Name[0] == '<')
{
continue;
}
var exposed = fi.IsPublic || fi.GetCustomAttribute<SerializeField>() != null;
if (!exposed)
{
@ -246,11 +255,39 @@ namespace UltraCombos
result.Add(fi);
}
}
foreach (var pi in t.GetProperties(propFlags | BindingFlags.DeclaredOnly))
{
// Must have a getter and no index parameters.
if (!pi.CanRead || pi.GetIndexParameters().Length > 0)
{
continue;
}
if (pi.GetCustomAttribute<HideInInspector>() != null)
{
continue;
}
if (!IsSupported(pi.PropertyType))
{
continue;
}
if (!result.Any(r => r.Name == pi.Name))
{
result.Add(pi);
}
}
}
return result;
}
private static Type GetMemberType(MemberInfo mi) =>
mi is FieldInfo fi ? fi.FieldType : ((PropertyInfo)mi).PropertyType;
private static bool IsUnityBuiltIn(Type t) =>
t.Namespace != null &&
t.Namespace.StartsWith("UnityEngine", StringComparison.Ordinal);
private static string FriendlyType(Type t)
{
if (t == typeof(float)) { return "float"; }

@ -0,0 +1,285 @@
using System;
using System.Collections.Generic;
using System.Globalization;
using System.Reflection;
using System.Text;
using TMPro;
using UnityEngine;
namespace UltraCombos
{
/// <summary>
/// Read-only runtime monitor that renders selected component fields and properties
/// into a single TextMeshProUGUI component using TMP rich-text colour tags.
/// Drag the target TextMeshProUGUI into the Display Text field in the Inspector.
/// </summary>
[ExecuteAlways]
public class DebugMonitorHUD : MonoBehaviour
{
// ── Data ──────────────────────────────────────────────────────────────────
[Serializable]
public class Entry
{
public UnityEngine.Object target;
public string groupLabel = string.Empty;
/// <summary>Field/property names written by the custom Editor.</summary>
public List<string> fields = new();
}
[Header("Display")]
[SerializeField] private TextMeshProUGUI displayText;
[Header("General")]
[Tooltip("How many times per second the text is refreshed. 0 = every frame.")]
[Range(0f, 60f)]
public float refreshRate = 20f;
[Header("Entries")]
public List<Entry> entries = new();
// ── Runtime state ─────────────────────────────────────────────────────────
private float nextRefreshTime;
private readonly StringBuilder stringBuilder = new();
// ── TMP colour tokens ─────────────────────────────────────────────────────
private const string colHeader = "#FFC940";
private const string colLabel = "#AAAAAA";
private const string colFloat = "#7EC8E3";
private const string colInt = "#7EC8E3";
private const string colBoolTrue = "#7EE37E";
private const string colBoolFalse = "#E37E7E";
private const string colString = "#E3D47E";
private const string colEnum = "#C8A0E3";
private const string colVec = "#E3C87E";
private const string colColor = "#E3E3E3";
private const string colReadonly = "#666666";
// ── Unity callbacks ───────────────────────────────────────────────────────
private void Update()
{
if (displayText == null)
{
return;
}
if (refreshRate > 0f && Time.unscaledTime < nextRefreshTime)
{
return;
}
nextRefreshTime = Time.unscaledTime + (refreshRate > 0f ? 1f / refreshRate : 0f);
RebuildText();
}
// ── Text building ─────────────────────────────────────────────────────────
private void RebuildText()
{
stringBuilder.Clear();
var first = true;
foreach (var entry in entries)
{
if (entry == null || entry.target == null || entry.fields.Count == 0)
{
continue;
}
if (!first)
{
stringBuilder.AppendLine();
}
first = false;
AppendEntry(entry);
}
displayText.SetText(stringBuilder);
}
private void AppendEntry(Entry entry)
{
if (!string.IsNullOrEmpty(entry.groupLabel))
{
stringBuilder
.Append("<b><color=").Append(colHeader).Append('>')
.Append(entry.groupLabel)
.AppendLine("</color></b>");
}
var type = entry.target.GetType();
const BindingFlags fieldFlags =
BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance;
const BindingFlags propFlags =
BindingFlags.Public | BindingFlags.Instance;
foreach (var name in entry.fields)
{
// Resolve by name — check fields first, then properties.
var mi = (MemberInfo)type.GetField(name, fieldFlags) ??
type.GetProperty(name, propFlags);
if (mi == null)
{
continue;
}
try
{
AppendMember(entry.target, mi);
}
catch
{
// Skip members that throw during reflection.
}
}
}
private void AppendMember(object obj, MemberInfo mi)
{
var label = NicifyName(mi.Name);
var val = GetMemberValue(mi, obj);
var ftype = GetMemberType(mi);
stringBuilder
.Append("<color=").Append(colLabel).Append('>')
.Append(label)
.Append("</color> ");
AppendValue(val, ftype);
stringBuilder.AppendLine();
}
private void AppendValue(object val, Type ftype)
{
if (ftype == typeof(bool))
{
var b = (bool)val;
stringBuilder
.Append("<color=").Append(b ? colBoolTrue : colBoolFalse).Append('>')
.Append(b ? "true" : "false")
.Append("</color>");
return;
}
if (ftype == typeof(float))
{
stringBuilder
.Append("<color=").Append(colFloat).Append('>')
.Append(((float)val).ToString("G5", CultureInfo.InvariantCulture))
.Append("</color>");
return;
}
if (ftype == typeof(int))
{
stringBuilder
.Append("<color=").Append(colInt).Append('>')
.Append(((int)val).ToString(CultureInfo.InvariantCulture))
.Append("</color>");
return;
}
if (ftype == typeof(string))
{
stringBuilder
.Append("<color=").Append(colString).Append(">\"")
.Append(val as string ?? string.Empty)
.Append("\"</color>");
return;
}
if (ftype == typeof(Vector2))
{
var v = (Vector2)val;
stringBuilder
.Append("<color=").Append(colVec).Append(">(")
.Append(v.x.ToString("G4", CultureInfo.InvariantCulture)).Append(", ")
.Append(v.y.ToString("G4", CultureInfo.InvariantCulture))
.Append(")</color>");
return;
}
if (ftype == typeof(Vector3))
{
var v = (Vector3)val;
stringBuilder
.Append("<color=").Append(colVec).Append(">(")
.Append(v.x.ToString("G4", CultureInfo.InvariantCulture)).Append(", ")
.Append(v.y.ToString("G4", CultureInfo.InvariantCulture)).Append(", ")
.Append(v.z.ToString("G4", CultureInfo.InvariantCulture))
.Append(")</color>");
return;
}
if (ftype == typeof(Color))
{
var c = (Color)val;
var hex = ColorUtility.ToHtmlStringRGBA(c);
// Coloured swatch character followed by the hex value.
stringBuilder
.Append("<color=#").Append(hex).Append(">■</color> ")
.Append("<color=").Append(colColor).Append(">#").Append(hex).Append("</color>");
return;
}
if (ftype.IsEnum)
{
stringBuilder
.Append("<color=").Append(colEnum).Append('>')
.Append(val?.ToString() ?? "?")
.Append("</color>");
return;
}
// Fallback: display the ToString() value as read-only italic text.
stringBuilder
.Append("<color=").Append(colReadonly).Append("><i>")
.Append(val?.ToString() ?? "(null)")
.Append("</i></color>");
}
// ── Reflection helpers ────────────────────────────────────────────────────
private static Type GetMemberType(MemberInfo mi) =>
mi is FieldInfo fi ? fi.FieldType : ((PropertyInfo)mi).PropertyType;
private static object GetMemberValue(MemberInfo mi, object obj) =>
mi is FieldInfo fi ? fi.GetValue(obj) : ((PropertyInfo)mi).GetValue(obj);
/// <summary>Converts a camelCase or _prefixed field name to a readable label.</summary>
private static string NicifyName(string s)
{
var start = 0;
while (start < s.Length && s[start] == '_')
{
start++;
}
if (s.Length > start + 1 && s[start] == 'm' && s[start + 1] == '_')
{
start += 2;
}
var sb = new StringBuilder();
for (var i = start; i < s.Length; i++)
{
var c = s[i];
if (i == start)
{
sb.Append(char.ToUpper(c));
continue;
}
if (char.IsUpper(c) && !char.IsUpper(s[i - 1]))
{
sb.Append(' ');
}
sb.Append(c);
}
return sb.ToString();
}
}
}

@ -0,0 +1,2 @@
fileFormatVersion: 2
guid: 705242ff475ca7b4bbc7341c5c7218ac

@ -9,8 +9,8 @@ namespace UltraCombos
{
/// <summary>
/// Runtime settings panel. Press the configured toggle key (default F1) to show or hide.
/// Drag any Component into the Entries list via the Inspector, select the fields to expose,
/// and the panel lets you tweak them live at runtime.
/// Drag any Component into the Entries list via the Inspector, select the fields or
/// properties to expose, and the panel lets you tweak them live at runtime.
/// </summary>
public class DebugSettingsGUI : MonoBehaviour
{
@ -22,7 +22,7 @@ namespace UltraCombos
public UnityEngine.Object target;
public string groupLabel = string.Empty;
/// <summary>Field names written by the custom Editor.</summary>
/// <summary>Field/property names written by the custom Editor.</summary>
public List<string> fields = new();
}
@ -107,66 +107,70 @@ namespace UltraCombos
}
var type = entry.target.GetType();
const BindingFlags flags =
const BindingFlags fieldFlags =
BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance;
const BindingFlags propFlags =
BindingFlags.Public | BindingFlags.Instance;
foreach (var name in entry.fields)
{
var fi = type.GetField(name, flags);
if (fi == null)
// Resolve by name — check fields first, then properties.
var mi = (MemberInfo)type.GetField(name, fieldFlags) ??
type.GetProperty(name, propFlags);
if (mi == null)
{
continue;
}
try
{
DrawField(entry.target, fi);
DrawMember(entry.target, mi);
}
catch
{
// Skip fields that throw during reflection.
// Skip members that throw during reflection.
}
}
}
// ── Per-type drawing ──────────────────────────────────────────────────────
private void DrawField(object obj, FieldInfo fi)
private void DrawMember(object obj, MemberInfo mi)
{
var val = fi.GetValue(obj);
var ftype = fi.FieldType;
var label = NicifyName(fi.Name);
var val = GetMemberValue(mi, obj);
var ftype = GetMemberType(mi);
var label = NicifyName(mi.Name);
if (ftype == typeof(bool))
{
DrawBool(obj, fi, (bool)val, label);
DrawBool(obj, mi, (bool)val, label);
}
else if (ftype == typeof(float))
{
DrawFloat(obj, fi, (float)val, label);
DrawFloat(obj, mi, (float)val, label);
}
else if (ftype == typeof(int))
{
DrawInt(obj, fi, (int)val, label);
DrawInt(obj, mi, (int)val, label);
}
else if (ftype == typeof(string))
{
DrawString(obj, fi, (string)val, label);
DrawString(obj, mi, (string)val, label);
}
else if (ftype == typeof(Vector2))
{
DrawVector2(obj, fi, (Vector2)val, label);
DrawVector2(obj, mi, (Vector2)val, label);
}
else if (ftype == typeof(Vector3))
{
DrawVector3(obj, fi, (Vector3)val, label);
DrawVector3(obj, mi, (Vector3)val, label);
}
else if (ftype == typeof(Color))
{
DrawColor(obj, fi, (Color)val, label);
DrawColor(obj, mi, (Color)val, label);
}
else if (ftype.IsEnum)
{
DrawEnum(obj, fi, val, ftype, label);
DrawEnum(obj, mi, val, ftype, label);
}
else
{
@ -179,7 +183,7 @@ namespace UltraCombos
}
}
private void DrawBool(object obj, FieldInfo fi, bool val, string label)
private void DrawBool(object obj, MemberInfo mi, bool val, string label)
{
using (new GUILayout.HorizontalScope())
{
@ -187,14 +191,14 @@ namespace UltraCombos
var next = GUILayout.Toggle(val, val ? "✓" : string.Empty);
if (next != val)
{
fi.SetValue(obj, next);
SetMemberValue(mi, obj, next);
}
}
}
private void DrawFloat(object obj, FieldInfo fi, float val, string label)
private void DrawFloat(object obj, MemberInfo mi, float val, string label)
{
var range = fi.GetCustomAttribute<RangeAttribute>();
var range = mi.GetCustomAttribute<RangeAttribute>();
using (new GUILayout.HorizontalScope())
{
GUILayout.Label(label, labelStyle, GUILayout.Width(150));
@ -213,14 +217,14 @@ namespace UltraCombos
if (!Mathf.Approximately(next, val))
{
fi.SetValue(obj, next);
SetMemberValue(mi, obj, next);
}
}
}
private void DrawInt(object obj, FieldInfo fi, int val, string label)
private void DrawInt(object obj, MemberInfo mi, int val, string label)
{
var range = fi.GetCustomAttribute<RangeAttribute>();
var range = mi.GetCustomAttribute<RangeAttribute>();
using (new GUILayout.HorizontalScope())
{
GUILayout.Label(label, labelStyle, GUILayout.Width(150));
@ -239,12 +243,12 @@ namespace UltraCombos
if (next != val)
{
fi.SetValue(obj, next);
SetMemberValue(mi, obj, next);
}
}
}
private void DrawString(object obj, FieldInfo fi, string val, string label)
private void DrawString(object obj, MemberInfo mi, string val, string label)
{
using (new GUILayout.HorizontalScope())
{
@ -252,12 +256,12 @@ namespace UltraCombos
var next = GUILayout.TextField(val ?? string.Empty);
if (next != val)
{
fi.SetValue(obj, next);
SetMemberValue(mi, obj, next);
}
}
}
private void DrawVector2(object obj, FieldInfo fi, Vector2 val, string label)
private void DrawVector2(object obj, MemberInfo mi, Vector2 val, string label)
{
using (new GUILayout.HorizontalScope())
{
@ -269,12 +273,12 @@ namespace UltraCombos
var next = new Vector2(x, y);
if (next != val)
{
fi.SetValue(obj, next);
SetMemberValue(mi, obj, next);
}
}
}
private void DrawVector3(object obj, FieldInfo fi, Vector3 val, string label)
private void DrawVector3(object obj, MemberInfo mi, Vector3 val, string label)
{
using (new GUILayout.HorizontalScope())
{
@ -288,12 +292,12 @@ namespace UltraCombos
var next = new Vector3(x, y, z);
if (next != val)
{
fi.SetValue(obj, next);
SetMemberValue(mi, obj, next);
}
}
}
private void DrawColor(object obj, FieldInfo fi, Color val, string label)
private void DrawColor(object obj, MemberInfo mi, Color val, string label)
{
GUILayout.Label(label, headerStyle);
using (new GUILayout.VerticalScope(subBoxStyle))
@ -305,7 +309,7 @@ namespace UltraCombos
var next = new Color(r, g, b, a);
if (next != val)
{
fi.SetValue(obj, next);
SetMemberValue(mi, obj, next);
}
}
}
@ -321,7 +325,7 @@ namespace UltraCombos
}
}
private void DrawEnum(object obj, FieldInfo fi, object val, Type type, string label)
private void DrawEnum(object obj, MemberInfo mi, object val, Type type, string label)
{
var names = Enum.GetNames(type);
var values = Enum.GetValues(type);
@ -336,7 +340,7 @@ namespace UltraCombos
var next = GUILayout.SelectionGrid(cur, names, names.Length);
if (next != cur)
{
fi.SetValue(obj, values.GetValue(next));
SetMemberValue(mi, obj, values.GetValue(next));
}
}
else
@ -351,12 +355,30 @@ namespace UltraCombos
{
cur = (cur + 1) % names.Length;
}
fi.SetValue(obj, values.GetValue(Mathf.Clamp(cur, 0, values.Length - 1)));
SetMemberValue(mi, obj, values.GetValue(Mathf.Clamp(cur, 0, values.Length - 1)));
}
}
}
// ── Helpers ───────────────────────────────────────────────────────────────
// ── Reflection helpers ────────────────────────────────────────────────────
private static Type GetMemberType(MemberInfo mi) =>
mi is FieldInfo fi ? fi.FieldType : ((PropertyInfo)mi).PropertyType;
private static object GetMemberValue(MemberInfo mi, object obj) =>
mi is FieldInfo fi ? fi.GetValue(obj) : ((PropertyInfo)mi).GetValue(obj);
private static void SetMemberValue(MemberInfo mi, object obj, object val)
{
if (mi is FieldInfo fi)
{
fi.SetValue(obj, val);
}
else if (mi is PropertyInfo pi && pi.CanWrite)
{
pi.SetValue(obj, val);
}
}
private static float ParseFloat(string s, float fallback)
=> float.TryParse(s, NumberStyles.Float, CultureInfo.InvariantCulture, out var v) ? v : fallback;

@ -19,7 +19,7 @@ namespace UltraCombos
[SerializeField] private bool exitOnEsc = true;
private float timestamp = 0;
[field: SerializeField, ReadOnly] public float CurrentFps { get; private set; } = 30.0f;
[field: SerializeField, ReadOnly] public float CurrentFps { get; private set; } = 60.0f;
[Header("Event")]
public UnityEvent onQuit = new UnityEvent();

@ -2,7 +2,8 @@
"name": "UltraCombos.GiantMuseum.Runtime",
"rootNamespace": "UltraCombos",
"references": [
"GUID:75469ad4d38634e559750d17036d5f7c"
"GUID:75469ad4d38634e559750d17036d5f7c",
"Unity.TextMeshPro"
],
"includePlatforms": [],
"excludePlatforms": [],

@ -22,11 +22,12 @@
<Project Path="Unity.InputSystem.ForUI.csproj" />
<Project Path="Unity.Collections.CodeGen.csproj" />
<Project Path="Unity.Mathematics.Editor.csproj" />
<Project Path="UltraCombos.GiantMuseum.Editor.csproj" />
<Project Path="Klak.Spout.Runtime.csproj" />
<Project Path="HBAO.Universal.Runtime.csproj" />
<Project Path="Unity.InputSystem.TestFramework.csproj" />
<Project Path="Assembly-CSharp.csproj" />
<Project Path="UltraCombos.GiantMuseum.Runtime.csproj" />
<Project Path="Assembly-CSharp.csproj" />
<Project Path="Metatex.Editor.csproj" />
<Project Path="Klak.Spout.Editor.csproj" />
<Project Path="Unity.USD.Core.csproj" />
@ -34,7 +35,6 @@
<Project Path="OscJack.Editor.csproj" />
<Project Path="Unity.InputSystem.DocCodeSamples.csproj" />
<Project Path="Sirenix.OdinInspector.Modules.UnityMathematics.csproj" />
<Project Path="UltraCombos.GiantMuseum.Editor.csproj" />
<Project Path="HBAO.Runtime.csproj" />
<Project Path="HBAO.Editor.csproj" />
<Project Path="HBAO.Universal.Editor.csproj" />

Loading…
Cancel
Save